Appearance
题目
某银行提供 1 个服务窗口和 10 个顾客等待座位。顾客到达银行时,若有空座位,则到取号机领取一个号,等待叫号。取号机一次仅允许一位顾客使用。当营业员空闲时,通过叫号选取一位顾客并为其服务。
顾客和营业员的活动过程描述如下:
cobegin
process 顾客 i {
从取号机获得一个号码;
等待叫号;
获得服务;
}
process 营业员 {
while (TRUE) {
叫号;
为顾客服务;
}
}
coend请添加必要的信号量和 P、V 操作实现互斥和同步。要求写出完整的过程,说明信号量的含义并赋初值。
解析
关系拆解
| 约束 | 类型 | 信号量 | 初值 |
|---|---|---|---|
| 等待座位最多 10 个 | 同步(容量) | empty | 10 |
| 取号机一次一人 | 互斥 | mutex | 1 |
| 营业员要等有顾客才能叫号 | 同步:顾客 → 营业员 | full | 0 |
| 顾客要等被叫号才能接受服务 | 同步:营业员 → 顾客 | service | 0 |
4 个信号量——比一般"生产者消费者"多一个,因为叫号 + 接受服务构成顾客和营业员之间的双向同步。
信号量与代码
c
semaphore empty = 10; // 空座位的数量
semaphore mutex = 1; // 取号机互斥
semaphore full = 0; // 已坐着等待的顾客数
semaphore service = 0; // "已被叫号"的事件信号
process 顾客 i {
P(empty); // 等空座位(无座要走?题目不允许排到外面,按"等到有座"处理)
P(mutex); // 占用取号机
从取号机取号;
V(mutex); // 让出取号机
V(full); // 通知营业员:又来了一个顾客
P(service); // 等待自己被叫号
接受服务;
}
process 营业员 {
while (TRUE) {
P(full); // 等有顾客
叫号;
V(empty); // 该顾客离开座位 → 多出一个空位
V(service); // 通知顾客:你被叫到了
为顾客服务;
}
}信号量含义说明(答题必写)
| 信号量 | 初值 | 含义 |
|---|---|---|
empty | 10 | 空座位数(最多 10 个等待顾客) |
mutex | 1 | 取号机互斥锁 |
full | 0 | 已就座等待叫号的顾客数 |
service | 0 | 营业员叫号事件信号;被叫顾客拿到一个 |
易错点
编者注(关键设计点):
V(empty)必须放在叫号后、不能在顾客接受服务后——因为顾客被叫到后离开座位、走到柜台,座位立刻就空了,不必等服务结束。如果把 V(empty) 放在顾客的"接受服务"之后,会浪费座位资源。这是评分细节里的常见扣分点。不要让"接受服务"包在 mutex 里——题目明确"取号机一次一人",没说"服务一次一人"。其实窗口本身就是一个,不需要互斥(窗口的串行由
full信号量自然保证)。
service信号量是必需的——很多同学省略 service 直接让营业员"叫号 + 服务"一气呵成,但这样做对顾客自己而言没有同步信号——顾客不知道什么时候轮到自己,只能"傻等"。service提供这种唤醒机制。顾客和营业员是 1:N 关系——多个顾客并发执行
process 顾客 i,但只有一个营业员。所以 mutex 只在多顾客之间需要(取号机互斥);营业员一人不需要给自己加锁。若考虑"满座顾客离开" —— 题面没明示离开行为。标答按"全部进队等"处理。如果题目说"满座顾客直接离开",则把 P(empty) 改成"non-blocking"(即 try-P)即可——但这超出了 408 题的范畴。