张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆一根筷子,桌子的中间是一碗米饭。哲学家们倾注毕生的精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿时,才试图拿起左、右两根筷子(一根一根地拿起)。如果筷子已在他人手上,则需等待。饥饿的哲学家只有同时拿起两根筷子才可以开始进餐,当进餐完毕后,放下筷子继续思考。

此时每个哲学家都必须持有两个临界资源,才能吃饭。
Code
semaphore chopstick[5] = {1, 1, 1, 1, 1};
Pi ()
{
while (1)
{
P(chopstick[i]);//拿左
P(chopstick[(i + 1) % 5]); //拿右
//吃饭...
V(chopstick[i]);//放左
V(chopstick[(i + 1) % 5]);//放右
//思考...
}
}
如果5个哲学家并发地拿起了自己左手边的筷子,那么当想拿起自己右手边的筷子的时候,发现都被阻塞了,每位哲学家循环等待右边的人放下筷子(阻塞),发生“死锁”,所以以上这种代码是不能解决问题的。
如何防止死锁的发生呢?
//情况3的code
semaphore chopstick[5] = {1, 1, 1, 1, 1};
semaphore mutex = 1;//互斥地取筷子
Pi () //i号哲学家的进程
{
while (1)
{
P(mutex);
P(chopstick[i]);//拿左
P(chopstick[(i + 1) % 5]); //拿右
V(mutex);
//吃饭...
V(chopstick[i]);//放左
V(chopstick[(i + 1) % 5]);//放右
//思考...
}
}
因此这种方法并不能保证只有两边的筷子都可用时,才允许哲学家拿起筷子。
举个例子:
此时4号右边的筷子不可用,但4号仍然会先拿起左边的筷子。
更准确的说法应该是:各哲学家拿筷子这件事必须互斥的执行。这就保证了即使一个哲学家在拿筷子拿到一半时被阻塞,也不会有别的哲学家会继续尝试拿筷子。这样的话,当前正在吃饭的哲学家放下筷子后,被阻塞的哲学家就可以获得等待的筷子了。
哲学家进餐问题的关键在于解决进程死锁。
这些进程之间只存在互斥关系,但是与之前接触到的互斥关系不同的是,每个进程都需要同时持有两个临界资源,因此就有“死锁”问题的隐患。如果在考试中遇到了一个进程需要同时持有多个临界资源的情况,应该参考哲学家问题的思想,分析题中给出的进程之间是否会发生循环等待,是否会发生死锁。可以参考哲学家就餐问题解决死锁的三种思路。