Appearance
哲学家进餐问题
考情分析
哲学家进餐问题是死锁和同步的经典例题,408 真题中偶有考查。属于 🔥🔥 中高频考点。
前面的同步问题中,每个进程只需要一种资源。但如果每个进程需要同时持有两种资源才能工作,而资源又是共享的——就像五个人围着火锅,每人需要两根筷子才能吃菜——经典的死锁场景就出现了。
问题描述
5 个哲学家围坐在圆桌旁,桌上有 5 根筷子,每两个哲学家之间放一根。哲学家交替进行思考和进餐。
进餐时需要同时拿起左右两根筷子,用完后放回。
P0
C4 C0
P4 P1
C3 C1
P2
C2每根筷子是一个信号量:
semaphore chopstick[5] = {1, 1, 1, 1, 1};朴素方案(有死锁风险)
philosopher(i) {
while (true) {
think();
P(chopstick[i]); // 拿左边的筷子
P(chopstick[(i+1) % 5]); // 拿右边的筷子
eat();
V(chopstick[i]); // 放左边的筷子
V(chopstick[(i+1) % 5]); // 放右边的筷子
}
}死锁场景:5 个哲学家同时拿起左边的筷子 → 所有人都在等右边的筷子 → 没有人能吃到饭 → 死锁。
解决方案
方案一:限制人数
最多允许 4 个哲学家同时拿筷子:
semaphore limit = 4; // 最多 4 人同时尝试拿筷子
philosopher(i) {
think();
P(limit); // 限制人数
P(chopstick[i]);
P(chopstick[(i+1) % 5]);
eat();
V(chopstick[i]);
V(chopstick[(i+1) % 5]);
V(limit);
}方案二:奇偶策略
- 奇数号哲学家:先拿左边,再拿右边
- 偶数号哲学家:先拿右边,再拿左边
philosopher(i) {
think();
if (i % 2 == 1) { // 奇数号
P(chopstick[i]); // 先左后右
P(chopstick[(i+1) % 5]);
} else { // 偶数号
P(chopstick[(i+1) % 5]); // 先右后左
P(chopstick[i]);
}
eat();
V(chopstick[i]);
V(chopstick[(i+1) % 5]);
}破坏了循环等待条件,因为相邻哲学家拿筷子的顺序不同。
方案三:同时拿两根
用一个互斥信号量保证「拿两根筷子」是原子操作:
semaphore mutex = 1;
philosopher(i) {
think();
P(mutex);
P(chopstick[i]);
P(chopstick[(i+1) % 5]);
V(mutex);
eat();
V(chopstick[i]);
V(chopstick[(i+1) % 5]);
}缺点:一次只有一个哲学家能尝试拿筷子,并发度降低。
交互可视化
三种方案对比
| 方案 | 避免死锁的原理 | 并发度 |
|---|---|---|
| 限制人数 | 破坏「循环等待」(最多4人) | 较高 |
| 奇偶策略 | 破坏「循环等待」(拿筷子顺序不同) | 最高 |
| 同时拿两根 | 破坏「请求并保持」 | 最低 |
三大经典问题易错速查
把生产者-消费者 / 读者-写者 / 哲学家放到一张表里横向对比,是大题阅卷时区分"会写"与"写对"的关键:
| 问题 | 信号量初值 | 最关键的一行 | 最常见错误 |
|---|---|---|---|
| 生产者-消费者 | mutex=1, empty=N, full=0 | P 操作先同步后互斥:P(empty); P(mutex); 不能反 | P(mutex) 提前 → 缓冲区满时持锁阻塞 → 死锁 |
| 读者-写者(读优先) | rw_mutex=1, mutex=1, readcount=0 | 第一个读者 P(rw_mutex)、最后一个读者 V(rw_mutex) | 把 readcount++ 放到 mutex 外 → 写读交错 |
| 哲学家进餐 | chopstick[5]=1 | 5 人同时左手→右手 = 死锁 | 不限制人数 / 不奇偶 / 不用 mutex 包两次 P |
写 PV 大题的检查清单(写完务必逐条扫一遍):
- 每个 P 都有对应的 V — 漏一个 V 直接死锁
- 临界区里没有阻塞性调用 — 持锁阻塞 = 死锁种子
- 同步信号量的 P 在互斥信号量的 P 之前 — 顺序反 = 持锁阻塞
- 互斥信号量的 V 紧贴临界区结束 — V 太晚 = 并发度低
- 共享变量都被 mutex 保护 — readcount 这类反例最常被遗忘
- 死锁四条件中至少破坏一条 — 哲学家、银行家算法都按这条收尾
考研高频考点
- 🔥🔥🔥 朴素方案为什么会死锁
- 🔥🔥🔥 三种解决方案的 PV 操作写法
- 🔥🔥 各方案分别破坏了死锁的哪个必要条件
- 🔥 哲学家问题本质是死锁问题的体现
哲学家进餐问题是死锁的经典演示。下一篇系统地讨论死锁的定义、必要条件和预防策略。