哲学家就餐问题
哲学家就餐问题是由并发处理的先驱E.W.Dijkstra所提出,主要用于阐述死锁和无饥饿概念。假设五个哲学家一生只在思考和就餐。他们围坐在一个大圆桌旁,桌上有一大盘米饭。然而只有五根可用的筷子。所有的哲学家都在思考。若某个哲学家饿了,则拿起身边的两根筷子。如果他能够拿到这两根筷子,则可以就餐。当这个哲学家吃完后,又放下自己生变的两根筷子。如果他能够拿到这两根筷子,则可以就餐。当这个哲学家吃完后,又放下筷子继续思考。
- 试编写模仿哲学家就餐行为的程序,其中给每一个哲学家为一个线程而筷子则是共享对象。注意,必须防止出现两个哲学家同时使用一根筷子的情形。
- 修改所编写的程序,不允许出现死锁情况,也就是说,保证不会出现这样的情况:每个哲学家都已拥有一根筷子,并且在等待获得另一个人手中的筷子。
- 修改所编写的程序使得不会出现饥饿现象。
- 编写能够保证任意n个哲学家无饥饿就餐的程序。
解题思路:
- 筷子属于两个哲学家的共享资源,定义为全局变量。
- 这里的锁相关函数没有实现,这里就当做伪代码来看吧。
- 一个哲学家需要先看左手边的筷子,当左边的筷子没有人使用时拿起。
右边同理。
这时,哲学家有三个状态。
- 没有拿着筷子
- 拿到了一根筷子
- 幸运的拿到了两根筷子
这时,当处于③状态时,就可以开心的吃饭了。吃完以后,将筷子放下,让别人使用。
当处于②状态时,一直拿着这根筷子会制造饥饿。所以,在看到只能拿到一只筷子时,就再将它放下,可供其他人使用。
①状态,就只能等两边的人把筷子放下来了。
这个过程应该就能满足以上四个条件了。
代码
//全局的筷子信息
int Chopsticks[poilosophers]; //需要提前知道哲学家的人数,用动态数组在指定的初始化函数进行分配也行
/**
* poilosopherId 哲学家编号0,1,2,3,4
* poilosophers 哲学家个数,譬如5个
* /
void selectChopsticks(int poilosopherId, int poilosophers){
//1. l_chopstick左边的筷子,r_chopstick右边筷子
int l_chopstick = poilosopherId, r_chopstick = poilosopherId + 1;
//2. 保持环状
if (r_chopstick == poilosophers){
r_chopstick -= poilosophers;
}
int getChopstickNum = 0, eated = 0;
while (1){
lock(); //加锁
//3. 如果左边筷子空闲
if (Chopsticks[l_chopstick] == 0){
Chopsticks[l_chopstick] = poilosopherId;
}
unlock();
lock();
//4. 如果右侧筷子空闲
if (Chopsticks[r_chopstick] == 0){
Chopsticks[r_chopstick] = poilosopherId;
}
unlock();
//5. 如果拿到两个筷子,则吃饭
if (Chopsticks[l_chopstick] = poilosopherId && Chopsticks[r_chopstick] == poilosopherId) {
eated = 1;
}
//6. 回到初始状态0
//包含两种情况: 1. 拿到两个筷子,吃完后放下筷子; 2. 拿到一支筷子,则放下.
if (Chopsticks[l_chopstick] == poilosopherId){
Chopsticks[l_chopstick] = 0;
}
if (Chopsticks[r_chopstick] == poilosopherId){
Chopsticks[r_chopstick] = 0;
}
if (eated) break;
}
}
QA
- 以上代码是本人基于网上资料,加上自己的理解后的产物,仅仅为了更好的理解并发的死锁和无饥饿特性.