2.3.4_1 信号量机制
约 32 分钟
缓冲区容量和临界区占用要用不同信号量
上一课进程轮流获得 CPU,但切换可能发生在共享数据操作之间。容量2的生产者—消费者缓冲区用 empty=2、full=0、mutex=1。任务推演“生产A、生产B、消费一次”,并验证缓冲区与信号量一致。
class Buffer:
def __init__(self,capacity):
self.capacity=capacity; self.empty=capacity; self.full=0
self.items=[]
def produce(self,item):
if self.empty==0: return False
self.empty-=1 # P(empty)
self.items.append(item) # P(mutex)...V(mutex)内
self.full+=1 # V(full)
return True
def consume(self):
if self.full==0: return None
self.full-=1 # P(full)
item=self.items.pop(0)
self.empty+=1 # V(empty)
return item
b=Buffer(2)
assert b.produce("A") and b.produce("B")
assert not b.produce("C")
assert b.consume()=="A"
assert (b.items,b.empty,b.full)==(["B"],1,1)
初态 (empty,full)=(2,0);生产A后 (1,1),生产B后 (0,2),第三次生产必须等待;消费A后 (1,1)。始终有 empty+full=capacity,full 等于缓冲区项目数。真实并发代码中,修改队列还要包在 P(mutex) 与 V(mutex) 之间。
为什么顺序重要:生产者先 P(empty) 确保有槽,再 P(mutex) 进入临界区;若反过来先占 mutex 后因 empty=0 睡眠,消费者无法取得 mutex 释放槽,会死锁。消费者对 full 同理。
每次操作抽象时间 O(1),缓冲区空间 O(capacity)。典型错误:用一个信号量同时表示互斥和数量;忘记 V 导致资源永久减少;把 P/V 当普通整数自增减,忽略其原子性与阻塞/唤醒语义。
迁移答案:多个生产者仍共享同一 empty/full/mutex;mutex 初值1,empty 初值容量,full 初值0,不能按生产者数量各建一套计数。
下一课检查资源请求更一般的循环等待,银行家算法会在真正分配前试探系统是否仍处于安全状态。
小纸条
学完《2.3.4_1 信号量机制》后,请独立复现本课的核心状态变化或计算过程。
登录 后可看答案
Practice
本课练习
先独立作答再提交;编程题会在隔离沙箱中真实编译、运行并对拍。
本课练习正在补齐,暂不应标记为完成。