同步与互斥-操作系统
生产者–消费者问题
用到的概念:
- 信号量 (Semaphore) —— 用来同步和互斥,控制共享资源。
P()、V()操作 —— 分别是获取和释放信号量。- 互斥信号量 (
mutex) —— 用来保护临界区。 - 同步信号量 (
empty、full) —— 用来控制生产者和消费者之间的同步。
信号量
在标准操作系统教材(如《操作系统概念》Silberschatz)里,经典的「生产者-消费者问题」,用的信号量名字就是:
empty—— 空槽位数full—— 满槽位数mutex—— 缓冲区互斥锁
PV操作
这是标准的P/V操作,也叫信号量操作,源于操作系统理论。
P(s):对信号量s执行减1操作,如果s <= 0,调用P()的进程就阻塞等待。V(s):对信号量s执行加1操作,如果有因为这个信号量而阻塞的进程,会唤醒其中一个。
P(empty):
- 意义:检查是否还有空闲槽。
- 含义:
empty是一个计数信号量,值表示空闲槽位数。 - 当
empty > 0:能放入产品,值减1。 - 当
empty == 0:没有空闲槽,调用P(empty)的进程就阻塞,直到有消费者消费掉一件产品。
P(mutex):
- 意义:进入临界区。
- 含义:
mutex是一个互斥信号量,值是0或1。 - 当
mutex == 1:允许进入临界区,值减为0。 - 当
mutex == 0:表示已经有别的进程进入,调用P(mutex)的进程阻塞。
V(mutex):
- 意思:释放互斥锁。
- 执行完临界区后,调用
V(mutex)让其他被阻塞的进程有机会进入。 - 简而言之:
mutex值 +1,表示“我已经退出临界区,你们可以进来了。”
V(full):
- 意思:增加满槽数。
- 执行完放入操作后,调用
V(full)让因为P(full)而阻塞的消费者解锁。 - 简而言之:让消费者知道,“现在有一件产品可取了!”
例题
系统中有多个生产者进程和多个消费者进程,共享一个能存放1000件产品的环形缓冲区(初始为空)。缓冲区未满时,生产者进程可以放入其生产的一件产品,否则等待;缓冲区未空时,消费者进程可从缓冲区取走一件产品,否则等待。要求一个消费者进程从缓冲区连续取出10件产品后,其他消费者进程才可以取产品。请使用信号量PV操作实现进程间的互斥与同步,要求写出完整的过程,并说明所用信号量的含义和初值
信号量定义及初值
// 信号量及共享变量
semaphore empty = 1000; // 空闲槽位数,初值为1000
semaphore full = 0; // 满槽位数,初值为0
semaphore mutex = 1; // 缓冲区互斥锁,初值为1
semaphore consumer_turn = 1; // 控制消费者连续消费,初值为1
// 共享变量
int count_10 = 0; // 当前连续消费数
🟩 生产者代码
while (1) {
item = produce();
P(empty); // 检查是否还有空位
P(mutex); // 获取互斥锁
put_item(item); // 向缓冲区放入产品
V(mutex); // 释放互斥锁
V(full); // 增加满槽数,通知消费者
}
🟦 消费者代码
while (1) {
P(consumer_turn); // 获取消费权
for (int i = 0; i < 10; i++) {
P(full); // 检查是否有产品
P(mutex); // 获取互斥锁
item = remove_item(); // 从缓冲区取出产品
V(mutex); // 释放互斥锁
V(empty); // 增加空槽数,通知生产者
consume(item);
}
V(consumer_turn); // 完成10次消费,释放消费权
}
具体的应用
在实际编程里,它出现在:
- 线程池、协程池
- 消息队列、任务队列
- 网络IO、日志收集器
- 缓冲区管理

浙公网安备 33010602011771号