Dijkstra提出并解决的哲学家就餐问题是经典的进程同步问题。哲学家就餐问题描述如下:
有5个哲学家共用一张圆桌,分别坐在周围的5张椅子上,在圆桌上有5个碗和5只筷子,他们的生活方式是交替地进行思考和进餐。平时,每个哲学家进行思考,饥饿时便试图拿起其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐完毕,放下筷子继续思考。
桌子上的筷子f0,f1,…,f4是临界资源,应互斥使用,可用一个信号量表示一只筷子,5只筷子的5个信号量构成信号量数组,所有信号量的初值均为1。
struct semaphore chopstick[5] ;
chopstick[0].value=chopstick[1].value=1;
chopstick[2].value=chopstick[3].value=1 ;
chopstick[4].value=1 ;
每个哲学家算法流程:
(1) 拿起左、右筷子;
(2) 吃面条;
(3) 放下左、右筷子;
(4) 思考问题;
(5) 返回(1)。
则第i个哲学家的活动可描述如下:
cobegin
process Pi(i = 0,1,2,3,4)
{
while (TRUE)
{ wait(chopstick[i]); //拿起左边筷子
wait(chopstick[(i + 1)%5]); //拿起右边筷子
eating ;
signal(chopstick[i]); //放下左边筷子
signal(chopstick[(i+1)%5]);//放下右边筷子
thinking ;
}
}
coend
该方法虽然保证了临界资源的互斥使用,但是可能导致死锁。5个哲学家可能每个人都拿起自己的左筷子,但是却无法拿到自己的右筷子。既无法释放自己的筷子,也等不到别人的筷子完成自己的活动,最终形成死锁。
对于上述的死锁,可以采用以下的方法解决:
(1)至多允许4个哲学家同时取左边的筷子,这样能至少保证一个哲学家能就餐,并在用毕后释放他用过的两只筷子,从而使更多的哲学家能够进餐。
(2)仅当哲学家左右两只筷子均可用时,才允许他拿起筷子进餐。(用AND信号量机制)
(3)规定奇数号哲学家先拿左边筷子,然后再拿右边筷子;而偶数号哲学家先拿右边筷子,然后再拿左边筷子。
- 至多允许4个哲学家同时取左边的筷子算法
至多只允许四个哲学家同时进餐,以保证至少有一个哲学家能够进餐, 最终总会释放出他所使用过的两支筷子,从而可使更多的哲学家进餐。以下将room 作为信号量,只允许4 个哲学家同时进入餐厅就餐,这样就能保证至少有一个哲学家可以就餐,而申请进入餐厅的哲学家进入room 的等待队列,根据FIFO 的原则,总会进入到餐厅就餐,因此不会出现饿死和死锁的现象。
semaphore chopstick[5]={1,1,1,1,1};
semaphore room=4;
void philosopher(int i){
while(true){
think();
wait(room); //请求进入房间进餐
wait(chopstick[i]); //请求左手边的筷子
wait(chopstick[(i+1)%5]); //请求右手边的筷子
eat();
signal(chopstick[(i+1)%5]); //释放右手边的筷子
signal(chopstick[i]); //释放左手边的筷子
signal(room); //退出房间释放信号量room
}
}
2.当左右筷子均可用时(AND信号量)
仅当哲学家的左右两支筷子都可用时,才允许他拿起筷子进餐。 利用AND 型信号量机制实现:在一个原语中,将一段代码同时需要的多个临界资源,要么全部分配给它,要么一个都不分配,因此不会出现死锁的情形。当某些资源不够时阻塞调用进程;由于等待队列的存在,使得对资源的请求满足FIFO 的要求,因此不会出现饥饿的情形。
semaphore chopstick[5]={1,1,1,1,1};
void philosopher(int I){
while(true) {
think();
Swait(chopstick[(I+1)]%5, chopstick[I]);
eat();
Ssignal(chopstick[(I+1)]%5, chopstick[I]);
}
}
除了使用AND信号量,只用记录型信号量也可以。但是需要额外设置一个信号量。
仅当哲学家的左右两支筷子都可用时,才允许他拿起筷子进餐。 利用信号量的保护机制实现:通过信号量mutex对eat()之前的取左侧和右侧筷子的操作进行保护,使之成为一个原子操作,这样可以防止死锁的出现。
Mutex实际上是让每次都只有一个哲学家去拿左右筷子
当抢到左右筷子的这个哲学家吃饭时候,mutex就可以放其它的哲学家中的一个进来抢(or等待)他的左右筷子了)。
3.奇数号和偶数号方法
规定奇数号的哲学家先拿起他左边的筷子,然后再去拿他右边的筷子;而偶数号的哲学家则相反.按此规定,将是1,2号哲学家竞争1号筷子,3,4号哲学家竞争3号筷子.即五个哲学家都竞争奇数号筷子,获得后,再去竞争偶数号筷子。最后总会有一个哲学家能获得两支筷子而进餐。而申请不到的哲学家进入阻塞等待队列,根FIFO原则,则先申请的哲学家会较先可以吃饭,因此不会出现饿死的哲学家。