信号量机制实战:3人过桥问题与生产者-消费者模型代码实现对比

📅 2026/7/12 2:12:20
信号量机制实战:3人过桥问题与生产者-消费者模型代码实现对比
信号量机制实战3人过桥问题与生产者-消费者模型代码实现对比1. 信号量机制的核心原理与应用场景信号量Semaphore是操作系统中最基础的同步工具之一由Dijkstra在1965年提出。它本质上是一个整型变量配合两个原子操作wait()和signal()也称为P/V操作实现对共享资源的访问控制。信号量的核心特性原子性P/V操作执行过程不可中断阻塞机制当资源不可用时进程会自动进入等待队列状态记录通过计数器值反映资源可用数量等待队列管理因资源不足而阻塞的进程在Linux系统中信号量通过semaphore.h头文件提供支持主要API包括sem_init(sem_t *sem, int pshared, unsigned int value); sem_wait(sem_t *sem); // P操作 sem_post(sem_t *sem); // V操作 sem_destroy(sem_t *sem);提示信号量值与资源的关系值0表示可用资源数量值0资源已被占用无等待进程值0绝对值表示等待该资源的进程数2. 三人过桥问题的信号量解决方案2.1 问题建模与分析题目要求三人小华、小明、小亮需依次通过独木桥每次仅允许一人过桥过桥顺序强制小华→小亮→小明每人过桥后需更新共享白板上的物资计数关键同步需求桥资源的互斥访问每次一人过桥顺序的强制约束物资计数的原子更新2.2 完整C语言实现#include stdio.h #include pthread.h #include semaphore.h #define MASK_COUNT 200 sem_t bridge; // 桥互斥信号量 sem_t hua_done; // 小华完成信号 sem_t liang_done; // 小亮完成信号 int total_masks 0;// 共享物资计数器 void* hua(void* arg) { sem_wait(bridge); printf(小华开始过桥...\n); sleep(1); // 模拟过桥时间 printf(小华到达目的地\n); // 更新物资计数 total_masks MASK_COUNT; printf(小华更新白板当前物资%d\n, total_masks); sem_post(hua_done); sem_post(bridge); return NULL; } void* liang(void* arg) { sem_wait(hua_done); sem_wait(bridge); printf(小亮开始过桥...\n); sleep(1); printf(小亮到达目的地\n); total_masks MASK_COUNT; printf(小亮更新白板当前物资%d\n, total_masks); sem_post(liang_done); sem_post(bridge); return NULL; } void* ming(void* arg) { sem_wait(liang_done); sem_wait(bridge); printf(小明开始过桥...\n); sleep(1); printf(小明到达目的地\n); total_masks MASK_COUNT; printf(小明更新白板当前物资%d\n, total_masks); sem_post(bridge); return NULL; } int main() { sem_init(bridge, 0, 1); sem_init(hua_done, 0, 0); sem_init(liang_done, 0, 0); pthread_t t1, t2, t3; pthread_create(t1, NULL, hua, NULL); pthread_create(t2, NULL, liang, NULL); pthread_create(t3, NULL, ming, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); sem_destroy(bridge); sem_destroy(hua_done); sem_destroy(liang_done); return 0; }2.3 解决方案特点分析设计要素实现方案技术意义桥互斥bridge信号量(初值1)确保同一时间只有一人过桥顺序控制hua_done和liang_done信号量实现严格的过桥顺序约束物资计数共享变量临界区保护保证数据更新的原子性线程安全信号量操作包裹关键区域避免竞态条件3. 生产者-消费者模型的经典实现3.1 问题场景描述生产者线程生成数据并放入缓冲区消费者线程从缓冲区取出数据消费共享缓冲区容量有限的环形队列同步需求缓冲区满时生产者等待缓冲区空时消费者等待缓冲区访问需要互斥3.2 Java版本实现import java.util.LinkedList; import java.util.Queue; class BoundedBuffer { private final QueueInteger buffer; private final int capacity; private final Object lock new Object(); private int count 0; public BoundedBuffer(int capacity) { this.capacity capacity; this.buffer new LinkedList(); } public void produce(int item) throws InterruptedException { synchronized (lock) { while (count capacity) { lock.wait(); // 缓冲区满等待 } buffer.add(item); count; System.out.println(生产: item (库存: count )); lock.notifyAll(); // 唤醒可能等待的消费者 } } public int consume() throws InterruptedException { synchronized (lock) { while (count 0) { lock.wait(); // 缓冲区空等待 } int item buffer.remove(); count--; System.out.println(消费: item (库存: count )); lock.notifyAll(); // 唤醒可能等待的生产者 return item; } } } public class ProducerConsumer { public static void main(String[] args) { BoundedBuffer buffer new BoundedBuffer(5); // 生产者线程 Thread producer new Thread(() - { try { for (int i 1; i 10; i) { buffer.produce(i); Thread.sleep((int)(Math.random() * 1000)); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); // 消费者线程 Thread consumer new Thread(() - { try { for (int i 1; i 10; i) { buffer.consume(); Thread.sleep((int)(Math.random() * 1500)); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); producer.start(); consumer.start(); } }3.3 Python使用Condition实现import threading import time import random class BoundedBuffer: def __init__(self, capacity): self.capacity capacity self.buffer [] self.lock threading.Lock() self.not_empty threading.Condition(self.lock) self.not_full threading.Condition(self.lock) def put(self, item): with self.not_full: while len(self.buffer) self.capacity: self.not_full.wait() self.buffer.append(item) print(f生产: {item} (库存: {len(self.buffer)})) self.not_empty.notify() def get(self): with self.not_empty: while not self.buffer: self.not_empty.wait() item self.buffer.pop(0) print(f消费: {item} (库存: {len(self.buffer)})) self.not_full.notify() return item def producer(buffer): for i in range(1, 11): buffer.put(i) time.sleep(random.random()) def consumer(buffer): for _ in range(10): buffer.get() time.sleep(random.random() * 1.5) if __name__ __main__: buffer BoundedBuffer(5) p threading.Thread(targetproducer, args(buffer,)) c threading.Thread(targetconsumer, args(buffer,)) p.start() c.start() p.join() c.join()4. 两种模型的对比分析4.1 信号量使用策略对比对比维度三人过桥问题生产者-消费者模型主要信号量顺序控制信号量空/满计数信号量同步重点严格顺序执行资源数量管理互斥范围整个过桥过程仅缓冲区操作等待条件前驱任务完成资源可用性典型模式接力式任务执行并行生产消费4.2 并发控制模式差异三人过桥问题前驱依赖型同步线性执行流程信号量构成执行屏障角色行为高度确定生产者-消费者模型资源池管理模式非确定性执行顺序动态平衡机制角色行为相对独立4.3 适用场景建议选择三人过桥方案当任务有严格的先后顺序要求各执行单元存在依赖关系需要构建线性工作流程资源争用场景简单明确选择生产者-消费者方案当处理速度不一致的模块间通信需要缓冲层平衡负载生产消费行为可并行资源管理需要弹性控制5. 进阶应用与调试技巧5.1 常见问题排查指南死锁场景信号量P/V操作顺序不当忘记释放已获取的信号量嵌套锁获取顺序不一致条件变量使用缺少循环检查调试方法# Linux下查看线程状态 ps -eLf | grep [程序名] # 或使用gdb附加到进程 gdb -p [pid] (gdb) info threads (gdb) thread apply all bt5.2 性能优化策略减小临界区范围// 不推荐做法 sem_wait(mutex); /* 大量非共享操作 */ sem_post(mutex); // 推荐做法 /* 非共享操作 */ sem_wait(mutex); /* 最小化共享操作 */ sem_post(mutex);选择合适同步原语竞争激烈时考虑自旋锁IO密集型场景优选条件变量简单计数场景使用信号量避免优先级反转使用优先级继承协议设置合理的线程优先级限制高优先级线程的阻塞时间5.3 现代替代方案C标准库#include mutex #include condition_variable #include queue templatetypename T class ConcurrentQueue { std::queueT queue; std::mutex mtx; std::condition_variable cv; public: void push(T item) { std::lock_guardstd::mutex lock(mtx); queue.push(item); cv.notify_one(); } T pop() { std::unique_lockstd::mutex lock(mtx); cv.wait(lock, [this]{ return !queue.empty(); }); T item queue.front(); queue.pop(); return item; } };Go语言channelfunc producer(ch chan- int) { for i : 0; i 10; i { ch - i time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) } close(ch) } func consumer(ch -chan int) { for num : range ch { fmt.Printf(Consumed: %d\n, num) time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond) } } func main() { ch : make(chan int, 5) go producer(ch) consumer(ch) }