哲学家就餐问题
1965年,Dijkstra提出并解决了一个他称之为哲学家就餐的同步问题。这个问题可以简单地描述如下∶五个哲学家围坐在一张圆桌周围,每个哲学家面前都有一盘通心粉。由于通心粉很滑,所以需要两把叉子才能夹住。相邻两个盘子之间放有一把叉子。哲学家的动作包括思考和进餐,当一个哲学家觉得饿了时,他就试图分两次去取其左边和右边的叉子,每次拿一把,但不分次序。如果成功地得到了两把叉子,就开始吃饭,吃完后放下叉子继续思考。问∶能为每一个哲学家写一段描述其行为的程序,且决不会死锁吗?
方案1
#define N 5 // 哲学家个数
void philosopher(int i) {
// 哲学家编号:0 - 4
while(TRUE) {
think(); // 哲学家在思考
take_fork(i); // 去拿左边的叉子
take_fork((i + 1) % N); // 去拿右边的叉子
eat(); // 哲学家在进餐
put_fork(i); // 放下左边的叉子
put_fork((i + 1) % N); // 放下右边的叉子
}
}
如果是单线程,这段代码没有任何问题。但是如果是5个线程同时运行,假设都运行到了 P(fork[i]);
,所有的哲学家都拿到了左边的叉子,那么运行 P(fork[(i + 1) % N]);
时就会出现死锁。没有任何哲学家能够拿到右边的叉子。
方案2
为了避免死锁,拿不到右边的叉子时,就将左边的放下,这样就不会一直占有这个资源了。
#define N 5 // 哲学家个数
void philosopher(int i) {
// 哲学家编号:0 - 4
while(TRUE) {
think(); // 哲学家在思考
take_fork(i); // 去拿左边的叉子
if (fork((i + 1) % N)) {
// 右边的叉子还在吗?
take_fork((i + 1) % N); // 去拿右边的叉子
break; // 两个叉子均到手
} else {
put_fork(i); // 放下左边的叉子
wait_some_time(); // 等待一会
}