2.3.8【2014 统考真题】

图片[1] - 2.3.8【2014 统考真题】 - 鹿快
图片[2] - 2.3.8【2014 统考真题】 - 鹿快
好的,我们来详细解析这道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

semaphore mutex2 = 1;
// 用于实现消费者之间的互斥,保证一个消费者能连续取10个。

套路二:分层放置 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)
// 解锁缓冲区
V(empty)
// 通知空位数+1

外层释放:10次循环结束后,归还“本轮消费权”。

V(mutex2)
// 释放消费权


第四步:为什么是这样?(深度剖析与答案修正)

为什么需要两个Mutex?—— 职责分离


mutex
(或
mutex1
) 是一个操作锁 (Operation Lock)。它的职责是保护数据结构的完整性(比如
in
/
out
指针不被同时修改),作用范围小,锁定时间极短。
mutex2
是一个策略锁 (Policy Lock)。它的职责是实现题目提出的特殊业务逻辑(一次取10个),作用范围大,锁定时间长(贯穿10次取货)。混用一个锁是行不通的。如果消费者在循环外
P(mutex)
,循环后
V(mutex)
,那么在它取10个产品的漫长时间里,生产者将完全被阻塞,系统并发度极低,效率极差。

王道答案的瑕疵与更优的实现

王道答案中,
mutex1
只在生产者中使用,消费者进程中完全没有使用
mutex1
。这是一个严重的逻辑漏洞。生产者通过
mutex1
保护对
in
指针和缓冲区的写入,但消费者在没有加锁的情况下就读取
out
指针和缓冲区,这会导致数据竞争 (Race Condition)正确的做法是:所有访问共享缓冲区的进程(无论是生产者还是消费者)都应该使用同一个互斥锁来保护缓冲区。下面我将给出的标准答案会修正这个问题,使用一个
mutex_buf
来被生产者和消费者共同遵守。

P操作的顺序:
P(mutex2)
->
for { P(full) ... }

这个顺序是符合逻辑的:先获得“批量处理”的许可,然后再开始处理每一件“物料”。如果反过来,在循环里
P(mutex2)
,那就失去了“连续处理10个”的意义了。


第五步:考试怎么解决?(实战指南)

快速识别:读到“生产者”、“消费者”、“缓冲区”,立刻写下
empty
,
full
,
mutex
三件套的定义。定位关键词:找到题目的特殊要求,圈出“一个消费者”、“连续取出10件”、“其他消费者才可以”。分析关键词:“消费者”和“其他消费者”之间产生了互斥。这意味着需要一个专门给消费者用的锁。立刻定义
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
© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
Croiiy的头像 - 鹿快
评论 抢沙发

请登录后发表评论

    暂无评论内容