信号量机制中---生产者 - 消费者问题
本文----------------------基于B站王道计算机教育《操作系统》课程编写(类似笔记hh),部分图片来源于课程。
一、生产者 - 消费者问题(互斥、同步的综合)
生产者 - 消费者问题是进程同步与互斥的经典场景,该问题可描述为:系统中有一组生产者进程和一组消费者进程,它们通过一个初始为0,大小为 n 的缓冲区进行数据交互。生产者进程负责生产产品,并将其放入缓冲区;消费者进程则从缓冲区取出产品进行消费。其中,缓冲区作为临界资源,任何时刻只能有一个进程对其进行访问。
1. 关系分析:
在这个问题中,生产者与消费者进程之间存在两种关键关系:
- 同步关系:
- 当缓冲区已满(没有空闲缓冲区)时,生产者必须等待消费者取走产品,释放出空闲缓冲区后才能继续生产。这体现了 “消费者释放缓冲区操作(前操作)→生产者使用缓冲区操作(后操作)” 的同步关系。
- 当缓冲区为空(没有产品)时,消费者必须等待生产者生产出产品放入缓冲区后才能进行消费。这体现了 “生产者生产产品并放入缓冲区操作(前操作)→消费者从缓冲区取出产品操作(后操作)” 的同步关系。
- 互斥关系:缓冲区是临界资源,无论是生产者将产品放入缓冲区,还是消费者从缓冲区取出产品,这些对缓冲区的访问操作都必须互斥进行,以防止数据覆盖或错误读取等问题。
2. 确定 P、V 操作顺序
根据上述关系分析,结合信号量机制的 “前 V 后 P” 同步规则以及互斥操作逻辑,我们可以确定 P、V 操作的大致顺序:
生产者进程:
- 需要检查是否有空闲缓冲区(对应同步关系),执行 P (empty) 操作,若 empty 为 0,则表示没有空闲缓冲区,生产者进程阻塞等待。
- 为了互斥访问缓冲区,执行 P (mutex) 操作,对缓冲区加锁。
- 进行生产操作,将产品放入缓冲区。
- 释放对缓冲区的互斥访问,执行 V (mutex) 操作,解锁缓冲区。
- 通知消费者缓冲区中有了新产品,执行 V (full) 操作。
消费者进程:
- 需要检查缓冲区中是否有产品(对应同步关系),执行 P (full) 操作,若 full 为 0,则表示缓冲区为空,消费者进程阻塞等待。
- 为了互斥访问缓冲区,执行 P (mutex) 操作,对缓冲区加锁。
- 从缓冲区取出产品进行消费。
- 释放对缓冲区的互斥访问,执行 V (mutex) 操作,解锁缓冲区。
- 通知生产者缓冲区有了空闲位置,执行 V (empty) 操作。
3. 信号量设置:初值至关重要
为了实现上述同步与互斥关系,我们需要设置三个信号量:
- 互斥信号量 mutex:用于实现对缓冲区的互斥访问,初值设为 1,因为初始时缓冲区可被任何进程访问,且同一时刻只能有一个进程访问。
- 同步信号量 empty:表示空闲缓冲区的数量,初值设为 n,因为初始时缓冲区全部为空,即有 n 个空闲位置可供生产者放入产品。
- 同步信号量 full:表示产品的数量,也即非空缓冲区的数量,初值设为 0,因为初始时缓冲区中没有产品。
4. 易错点:
- P 操作顺序不能颠倒:在生产者进程中,必须先执行 P (empty),再执行 P (mutex);在消费者进程中,必须先执行 P (full),再执行 P (mutex)。若顺序颠倒,就可能出现死锁。例如,当缓冲区已满时,生产者先执行 P (mutex),占用了缓冲区,但此时没有空闲缓冲区(empty 为 0),生产者阻塞在 P (empty) 上;而消费者想要访问缓冲区取走产品,由于 mutex 已被生产者占用,消费者也阻塞在 P (mutex) 上,从而导致生产者和消费者相互等待,形成死锁。
- V 操作顺序可交换:两个 V 操作(如生产者进程中的 V (mutex) 和 V (full),消费者进程中的 V (mutex) 和 V (empty))顺序可以交换,因为 V 操作不会导致进程阻塞,只是释放资源或通知其他进程,交换顺序不影响整体逻辑。