![图片[1] - 2.3.8【2014 统考真题】 - 鹿快](https://img.lukuai.com/blogimg/20251109/104892b373604a529c1d3a7f80133923.png)
![图片[2] - 2.3.8【2014 统考真题】 - 鹿快](https://img.lukuai.com/blogimg/20251109/daddf1742221405aa854deac33ff2f16.png)
好的,我们来详细解析这道2014年的考研真题。这道题是“生产者-消费者”问题的经典升级版,它在标准模型的基础上增加了一个非常有趣的“批量处理”或“分组消费”的约束,考察了对多层次同步与互斥关系的理解。
第一步:重述题目原文
【2014 统考真题】系统中有多个生产者进程和多个消费者进程,共享一个能存放 1000 件产品的环形缓冲区(初始为空)。缓冲区未满时,生产者进程可以放入其生产的一件产品,否则等待;缓冲区未空时,消费者进程可从缓冲区取走一件产品,否则等待。要求一个消费者进程从缓冲区连续取出10 件产品后,其他消费者进程才可以取产品。请使用信号 P, V [或 wait(),signal()] 操作实现进程间的互斥与同步,要求写出完整的过程,并说明所用信号量的含义和初值。
第二步:知识体系是什么?
这道题的知识体系建立在经典的 “生产者-消费者模型” 之上,并引入了 “组互斥” 或 “批量操作” 的概念。
基础模型:生产者-消费者
角色:多个生产者,多个消费者。资源:一个大小为1000的环形缓冲区。基本同步关系:
生产者必须等待缓冲区有空位 () 才能生产。消费者必须等待缓冲区有产品 (
empty) 才能消费。
full
基本互斥关系:
对环形缓冲区的指针(如 ,
in)和数据区的访问是临界操作,需要互斥锁 (
out) 保护。
mutex
核心变种:消费者之间的互斥
新约束:“一个消费者进程……连续取出10件产品后,其他消费者进程才可以取产品。”分析:这个约束引入了一种新的互斥关系。它不是针对所有进程(生产者和消费者)对缓冲区的微观操作(放一个/取一个),而是专门针对消费者群体内部的宏观操作(取一批)。解决方案:需要引入一个新的、独立的互斥信号量,专门用来控制“哪个消费者有权进行它这一批次的消费”。一旦一个消费者获得了这个权利,它就会持有这个锁,直到完成10次取货,然后再释放锁,让其他消费者来竞争。
第三步:解题套路是什么?
套路一:先搭标准框架,再加特殊约束
处理标准部分:
识别出典型的生产者-消费者场景,立即写下解决该问题的三个“标配”信号量:
:计空位数。
empty
semaphore empty = 1000;:计产品数。
full
semaphore full = 0; (或
mutex):保护缓冲区的互斥锁。
mutex_buf
semaphore mutex = 1;
处理特殊约束部分:
分析新约束:“消费者之间要互斥地进行‘取10个’这个动作”。为这个新的互斥关系增加一个信号量。我们可以叫它 或像王道答案那样叫
mutex_consumer。
mutex2 // 用于实现消费者之间的互斥,保证一个消费者能连续取10个。
semaphore mutex2 = 1;
套路二:分层放置 P/V 操作
生产者进程:不受新规矩的影响,逻辑保持不变。
->
P(empty) ->
P(mutex) ->
put() ->
V(mutex)
V(full)
消费者进程:逻辑需要分层。
外层(策略层):首先,一个消费者要抢到“本轮消费权”。
// 抢占消费权
P(mutex2)
内层(操作层):抢到权利后,循环10次,每次都执行一次标准的“取货”操作。
在循环内部,每一次取货都要遵守最基本的同步和互斥规则:
for (i=0; i<10; i++) { ... }
// 等待有产品
P(full) // 锁住缓冲区 (注:王道答案此处有瑕疵,见下文分析)
P(mutex) // 取货
get() // 解锁缓冲区
V(mutex) // 通知空位数+1
V(empty)
外层释放:10次循环结束后,归还“本轮消费权”。
// 释放消费权
V(mutex2)
第四步:为什么是这样?(深度剖析与答案修正)
为什么需要两个Mutex?—— 职责分离
(或
mutex) 是一个操作锁 (Operation Lock)。它的职责是保护数据结构的完整性(比如
mutex1/
in指针不被同时修改),作用范围小,锁定时间极短。
out 是一个策略锁 (Policy Lock)。它的职责是实现题目提出的特殊业务逻辑(一次取10个),作用范围大,锁定时间长(贯穿10次取货)。混用一个锁是行不通的。如果消费者在循环外
mutex2,循环后
P(mutex),那么在它取10个产品的漫长时间里,生产者将完全被阻塞,系统并发度极低,效率极差。
V(mutex)
王道答案的瑕疵与更优的实现
王道答案中,只在生产者中使用,消费者进程中完全没有使用
mutex1。这是一个严重的逻辑漏洞。生产者通过
mutex1保护对
mutex1指针和缓冲区的写入,但消费者在没有加锁的情况下就读取
in指针和缓冲区,这会导致数据竞争 (Race Condition)。正确的做法是:所有访问共享缓冲区的进程(无论是生产者还是消费者)都应该使用同一个互斥锁来保护缓冲区。下面我将给出的标准答案会修正这个问题,使用一个
out来被生产者和消费者共同遵守。
mutex_buf
P操作的顺序: ->
P(mutex2)
for { P(full) ... }
这个顺序是符合逻辑的:先获得“批量处理”的许可,然后再开始处理每一件“物料”。如果反过来,在循环里 ,那就失去了“连续处理10个”的意义了。
P(mutex2)
第五步:考试怎么解决?(实战指南)
快速识别:读到“生产者”、“消费者”、“缓冲区”,立刻写下 ,
empty,
full 三件套的定义。定位关键词:找到题目的特殊要求,圈出“一个消费者”、“连续取出10件”、“其他消费者才可以”。分析关键词:“消费者”和“其他消费者”之间产生了互斥。这意味着需要一个专门给消费者用的锁。立刻定义
mutex。构建消费者逻辑:
semaphore mutex_consumer = 1;
画出外层框架: …
P(mutex_consumer)。在框架内画出循环:
V(mutex_consumer)。在循环内,填入标准的单次消费逻辑:
for i=0 to 9 { ... },
P(full),
P(mutex),
get,
V(mutex)。
V(empty)
构建生产者逻辑:确认生产者不受影响,写出标准代码。最终检查:通读一遍,确认两个的职责清晰,放置位置正确,没有死锁风险。
mutex
【完整标准答案 (修正版)】
1. 信号量定义及含义
资源信号量 :
empty
含义: 记录环形缓冲区中空闲单元的数目。初值: 1000。
资源信号量 :
full
含义: 记录环形缓冲区中已占用单元(产品)的数目。初值: 0。
互斥信号量 :
mutex_buf
含义: 用于保证生产者和消费者进程对环形缓冲区的互斥访问。初值: 1。
互斥信号量 :
mutex_consumer
含义: 用于保证任一时刻只有一个消费者进程可以从缓冲区取产品,实现“连续取10件”的策略。初值: 1。
2. 进程同步与互斥的伪代码实现
// 信号量及共享变量定义
semaphore empty = 1000;
semaphore full = 0;
semaphore mutex_buf = 1; // 保护缓冲区的互斥锁
semaphore mutex_consumer = 1; // 保证消费者连续取货的互斥锁
buffer buf[1000]; // 环形缓冲区
int in = 0, out = 0; // 缓冲区头尾指针
cobegin {
// 生产者进程
process Producer_i {
while(TRUE) {
produce(); // 生产一件产品
P(empty); // 等待并获取一个空闲单元
P(mutex_buf); // 锁定缓冲区
put an item into buf[in];
in = (in + 1) % 1000;
V(mutex_buf); // 解锁缓冲区
V(full); // 通知产品数加一
}
}
// 消费者进程
process Consumer_j {
while(TRUE) {
P(mutex_consumer); // 申请“本轮消费权”
for (int i = 0; i < 10; i++) {
P(full); // 等待并获取一件产品
P(mutex_buf); // 锁定缓冲区
get an item from buf[out];
out = (out + 1) % 1000;
V(mutex_buf); // 解锁缓冲区
V(empty); // 通知空闲单元数加一
}
V(mutex_consumer); // 归还“本轮消费权”
consume(); // 消费取出的10件产品
}
}
} coend





![[C++探索之旅] 第一部分第十一课:小练习,猜单词 - 鹿快](https://img.lukuai.com/blogimg/20251015/da217e2245754101b3d2ef80869e9de2.jpg)










暂无评论内容