Appearance
题目
系统中有多个生产者进程和多个消费者进程,共享一个能存放 1000 件产品的环形缓冲区(初始为空)。
约束:
- 缓冲区未满时,生产者进程可以放入其生产的一件产品;否则等待
- 缓冲区未空时,消费者进程可以从缓冲区取走一件产品;否则等待
- 要求一个消费者进程从缓冲区连续取出 10 件产品后,其他消费者进程才可以取产品
请使用信号量 P、V 操作实现进程间的互斥与同步,要求写出完整的过程,并说明所用信号量的含义和初值。
解析
关系拆解
标准生产者-消费者部分
| 约束 | 信号量 | 初值 |
|---|---|---|
| 缓冲区互斥 | buffer_mutex | 1 |
| 缓冲区空位 | empty | 1000 |
| 缓冲区已有产品数 | full | 0 |
本题特殊约束
"一个消费者连续取 10 件后,其他消费者才能取"
这相当于消费者之间对"取产品序列"的互斥——一旦某个消费者开始取,他要连续占用消费权 10 次,期间其他消费者不能插队。
需要新增 consumer_mutex 信号量:
- 消费者进入循环 10 次取产品之前 P(consumer_mutex)
- 取完 10 次之后 V(consumer_mutex)
| 信号量 | 初值 | 用途 |
|---|---|---|
consumer_mutex | 1 | "连续取 10 次"的消费者间互斥 |
信号量与代码
c
semaphore consumer_mutex = 1; // 消费者间"连续取 10 次"互斥
semaphore buffer_mutex = 1; // 缓冲区单次访问互斥
semaphore empty = 1000; // 缓冲区空位数
semaphore full = 0; // 缓冲区产品数
Producer() {
while (true) {
生产产品;
P(empty); // 等空位
P(buffer_mutex); // 占用缓冲区
将产品放入缓冲区;
V(buffer_mutex); // 让出缓冲区
V(full); // 通知消费者:又多一件
}
}
Consumer() {
while (true) {
P(consumer_mutex); // ★ 拿到"独占连续取 10 次"权限
for (int i = 0; i < 10; i++) {
P(full); // 等有产品
P(buffer_mutex); // 占用缓冲区
从缓冲区取出产品;
V(buffer_mutex); // 让出缓冲区
V(empty); // 通知生产者:多出空位
消费产品; // 消费可以放在临界区外
}
V(consumer_mutex); // ★ 让出"连续取"权限
}
}信号量含义说明(答题必写)
| 信号量 | 初值 | 含义 |
|---|---|---|
consumer_mutex | 1 | 消费者之间"连续取 10 件"的串行权 |
buffer_mutex | 1 | 缓冲区单次操作互斥(生产者和消费者都用) |
empty | 1000 | 缓冲区空闲位数 |
full | 0 | 缓冲区已有产品数 |
易错点
编者注(题面陷阱 + 关键技巧):
empty初值是 1000,不是 0——题目说缓冲区初始为空,所以"空位数 = 1000","产品数 = 0"。初值不要搞反:empty跟"空"位数挂钩、初值 = 容量;full跟"已有"件数挂钩、初值 = 0。
consumer_mutex必须包在 for 循环外:
- 正确:
P(consumer_mutex); for (i=0; i<10) {取一件}; V(consumer_mutex);—— 消费者 A 取 10 件期间,B 一直在 P(consumer_mutex) 上阻塞- 错误:
for (i=0; i<10) { P(consumer_mutex); 取; V(consumer_mutex); }—— 这样 A、B 会"轮流"取产品,每次只取 1 件,完全不满足"连续 10 件"
buffer_mutex和consumer_mutex必须分开——两者作用不同:
buffer_mutex:保护缓冲区数据结构(环形队列指针),生产者和消费者都用,单次操作互斥consumer_mutex:保护取产品的连续性,只有消费者用,10 次操作互斥用一个信号量同时管,要么连续 10 次会让生产者也卡住(无法生产),要么生产者能插队但消费者无法连续——两种坏处都有。
P(empty)必须在P(buffer_mutex)之前——经典死锁规避(与 2009-45 同理)。如果反过来,生产者拿了 mutex 但 empty=0,会死锁。若题目改成"缓冲区至少有 10 件产品消费者才能开始取"——评分说明说这种理解给 6 分(不全对)。正确理解是"任何时候只要缓冲区非空消费者就能取,但取要连续 10 次才让出"。本题不是"等到 10 件齐了再取"。看清"连续取出"四个字。
生产者之间不需要单独互斥信号量——
buffer_mutex已经管住了多生产者之间的冲突;不要再加producer_mutex,否则降低并发度且评分会扣"未做到尽可能少"的分。