【操作系统】经典同步问题:生产者-消费者

【操作系统】经典同步问题:生产者-消费者

考点频率:★★★★★(PV操作必考经典模型,下午题高频)
难度:⭐⭐⭐⭐
建议:理解 empty 和 full 两个同步信号量的作用,掌握 P 操作的顺序

1️⃣ 问题描述

生产者-消费者问题是操作系统中最经典的同步问题,描述如下:

  • 若干个生产者进程往一个共享缓冲区中生产数据(放入产品)
  • 若干个消费者进程从同一个共享缓冲区中消费数据(取出产品)
  • 缓冲区有大小限制(假设容量为 n)

核心约束

  1. 缓冲区满时,生产者必须等待(不能继续放)
  2. 缓冲区空时,消费者必须等待(不能继续取)
  3. 同一时刻只能有一个进程访问缓冲区(互斥)

这个问题的本质是同步 + 互斥的组合:生产者和消费者之间是同步关系(满则等、空则等),多个生产者和多个消费者之间是互斥关系(同时只能一人操作缓冲区)。

2️⃣ 信号量设计

解决生产者-消费者问题需要三个信号量

信号量初值作用
emptyn(缓冲区容量)表示空位的数量。生产者每次放入前 P(empty),消费者每次取出后 V(empty)
full0表示已有数据的数量。消费者每次取出前 P(full),生产者每次放入后 V(full)
mutex1互斥信号量,保证同一时刻只有一个进程操作缓冲区

其中emptyfull是同步信号量(初值0或n),mutex是互斥信号量(初值1)。

3️⃣ 代码实现

semaphore empty=n;// 空位数量,初值为缓冲区大小 nsemaphore full=0;// 已有数据数量,初值为0semaphore mutex=1;// 互斥信号量// 生产者进程Producer(){while(true){// 生产一个产品 itemP(empty);// 检查是否有空位,没有则阻塞P(mutex);// 进入临界区// 将 item 放入缓冲区V(mutex);// 离开临界区V(full);// 通知消费者:有数据了}}// 消费者进程Consumer(){while(true){P(full);// 检查是否有数据,没有则阻塞P(mutex);// 进入临界区// 从缓冲区取出一个数据 itemV(mutex);// 离开临界区V(empty);// 通知生产者:有空位了// 消费数据 item}}

4️⃣ P操作顺序为什么不能颠倒?

在上面的代码中,生产者先执行P(empty)再执行P(mutex),消费者先执行P(full)再执行P(mutex)

如果把顺序颠倒——先 P(mutex) 再 P(empty) 会怎样?

假设缓冲区已满(empty = 0):

  1. 生产者执行P(mutex)→ 成功进入临界区(mutex从1→0)
  2. 生产者执行P(empty)→ empty=0,生产者被阻塞
  3. 此时没有消费者能进入缓冲区(因为 mutex=0,所有消费者被挡在P(mutex)外面)
  4. 生产者等着消费者来消费,消费者却进不来

这就是死锁

规则:在PV操作中,P操作的顺序是:先申请“资源信号量”(同步),再申请“互斥信号量”。V操作的顺序可以反过来。

5️⃣ 信号量值的动态变化

以缓冲区容量 n=3 为例,观察信号量变化:

初始状态empty=3, full=0, mutex=1(缓冲区全空)

操作emptyfull含义
初始30全空
生产者放入1个21还有2个空位
生产者再放1个12还有1个空位
生产者再放1个03已满
消费者取走1个12又有1个空位

empty=0时,生产者再执行P(empty)会被阻塞,直到消费者取走数据后执行V(empty)唤醒。

6️⃣ 多个生产者和多个消费者的情况

上面的代码可以支持多个生产者进程多个消费者进程同时运行(生产者与生产者之间也需要互斥吗?需要,因为多个生产者可能同时往缓冲区放数据,会破坏数据结构)。

mutex保证了:

  • 生产者之间互斥
  • 消费者之间互斥
  • 生产者和消费者之间互斥

也就是说,同一时刻只有一个进程(无论是生产者还是消费者)在操作缓冲区。

7️⃣ 经典例题

例题1:某系统有一个容量为10的缓冲区,用PV操作实现生产者-消费者同步。信号量empty初值为10,full初值为0。若当前empty=3full=7,则表示( )。

A. 缓冲区中有3个数据,7个空位
B. 缓冲区中有7个数据,3个空位
C. 缓冲区已满
D. 缓冲区为空

解析empty表示空位数=3,full表示已有数据数=7。缓冲区容量=10,3+7=10,正常状态。选B


例题2:在生产者-消费者问题中,若将生产者代码中的P(empty)P(mutex)交换顺序,则可能导致( )。

A. 缓冲区数据丢失
B. 死锁
C. 消费者饥饿
D. 数据不一致

解析:先 P(mutex) 再 P(empty) 时,若缓冲区已满,生产者会持有 mutex 锁并阻塞在 empty 上,导致消费者无法进入临界区取数据,造成死锁。选B

8️⃣ 与单缓冲区的区别

对比项单缓冲区(容量1)多缓冲区(容量n)
empty初值1n
生产者等待条件缓冲区有数据时等待缓冲区满时等待
消费者等待条件缓冲区空时等待缓冲区空时等待
信号量数量2个(full, mutex)或2个(empty, full)3个(empty, full, mutex)

9️⃣ 记忆口诀

生产消费经典题,三个信号量搞定。
empty空位full数,mutex互斥保唯一。
先同步再互斥,P操作顺序别颠倒。
生产P空V满,消费P满V空。

🔟 小测验(评论区对答案)

某系统采用PV操作实现生产者-消费者同步,缓冲区容量为8。当前信号量值为:empty=2, full=6, mutex=1。此时若生产者执行一次P(empty)操作,则empty的值变为( )。若接着消费者执行一次V(empty)操作,则empty的值变为( )。
A. 1, 2
B. 1, 1
C. 2, 3
D. 0, 1

🔔本专栏日更2篇,点击头像 → 专栏《软考中级高频考点》订阅,第一时间接收新内容

#软考中级 #软件设计师 #生产者消费者 #PV操作 #进程同步 #操作系统