PV操作核心大题(生产者-消费者模型:水缸取水/倒水问题)
一、题目场景
某寺庙存在若干小和尚和老和尚,水井水量无限,水缸最大容量为12桶;水桶总数固定为N个,且满足以下约束:
-
水井同一时间仅允许1个小和尚打水;
-
水缸同一时间仅允许1个和尚(小/老)执行倒水/取水操作;
-
小和尚仅能在水缸未满时打水倒入,老和尚仅能在水缸有水时取水;
-
避免水桶被全部占用导致死锁(如老和尚空拿桶等水,小和尚无桶可用)。
请基于PV操作设计小和尚(打水倒水)和老和尚(取水)的进程逻辑,并说明各信号量的含义。
二、信号量定义(核心:信号量=进程执行某操作的“凭证”)
| 信号量名称 | 初始值 | 作为“执行凭证”的核心含义 |
|---|---|---|
empty |
12 | 小和尚的“倒水资格凭证”:表示水缸剩余可倒入的桶数,持有该凭证才能启动“打水倒入水缸”流程(P操作=消耗凭证,剩余容量-1) |
full |
0 | 老和尚的“取水资格凭证”:表示水缸中已有的水量桶数,持有该凭证才能启动“从水缸取水”流程(P操作=消耗凭证,已有水量-1) |
pail |
N | 所有和尚的“用桶资格凭证”:表示当前可用水桶数量,持有该凭证才能拿桶(P=消耗,V=归还) |
mutex_well |
1 | 小和尚的“水井独占凭证”:互斥信号量,持有该凭证才能独占水井打水(保证水井操作原子性) |
mutex_wat |
1 | 所有和尚的“水缸独占凭证”:互斥信号量,持有该凭证才能独占水缸操作(倒水/取水,保证水缸操作原子性) |
三、PV操作实现(408标准答题伪代码)
1. 小和尚进程(生产者:打水→倒入水缸)
1 | void xiao_heshang() { |
2. 老和尚进程(消费者:从水缸取水)
1 | void lao_heshang() { |
四、408高频提问与核心解析
-
为何
empty和full不能合并为一个信号量?
合并后无法同时约束“水缸不溢出”(小和尚的倒水前提)和“水桶死锁”(老和尚空拿桶):empty仅约束小和尚的“倒水资格”,确保水缸未满才会启动打水;full仅约束老和尚的“取水资格”,确保有水才会抢占水桶,避免水桶被无效占用。
-
老和尚进程中
P(full)必须在P(pail)之前的原因?
若先P(pail)再P(full):可能出现所有老和尚抢占完所有水桶,等待full(水缸无水),导致小和尚无桶可用,触发死锁;调整顺序后,只有“有取水资格”的老和尚才会抢占水桶,从根源避免死锁。 -
mutex_well/mutex_wat与empty/full的本质区别?mutex_*是互斥凭证:保证单个资源(水井/水缸)的临界区操作原子性,初始值恒为1;empty/full是同步凭证:约束生产者(小和尚)和消费者(老和尚)的执行顺序,初始值由资源总量(水缸容量)决定。
总结
-
信号量的“凭证属性”:同步信号量(
empty/full)是“执行前置条件的凭证”,互斥信号量(mutex_*)是“独占资源的凭证”; -
顺序原则:同步P操作(
P(full)/P(empty))必须在互斥P操作(P(pail)/P(mutex_*))之前,避免死锁; -
生产者-消费者模型核心:有容量上限的共享容器(如水缸),需用一对同步信号量(
empty/full)分别约束生产者和消费者,再用互斥信号量保证容器操作的原子性。