1.问题描述
一张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆一根筷子,桌子的中间是一碗米饭,如图所示。
哲学家们倾注毕生精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿的时候,才试图拿起左、 右两根筷子(一根一根地拿起)。如果筷子已在他人手上,则需等待。
饥饿的哲学家只有同时拿到了两根筷子才可以开始进餐,当进餐完毕后,放下筷子继续思考。
2.哲学家进餐无死锁算法
2.1 仅当哲学家的左右两支筷子都可用时,1.1才允许他拿起筷子进餐。
方法 1: 利用 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]);
}
}
方法 2: 利用信号量的保护机制实现。
通过信号量 mutex 对 eat() 之前的取左侧和右侧筷子的操作进行保护, 使之成为一个原子操作, 这样可以防止死锁的出现。
semaphore mutex = 1 ;
semaphore chopstick[5]={1, 1, 1, 1, 1};
void philosopher(int I){
while(true){
think();
wait(mutex);
wait(chopstick[(I+1)]%5);
wait(chopstick[I]);
signal(mutex);
eat();
signal(chopstick[(I+1)]%5);
signal(chopstick[I]);
}
}
这个代码有个问题就是同一时间只能有一个哲学家取筷子,效率比较低。但是可以防止死锁。
2.2 规定奇数号的哲学家先拿起他左边的筷子,然后再去拿他右边的筷子;而偶数号的哲学家则相反。
按此规定,将是 1,4 号哲学家竞争 4 号筷子,2,3 号哲学家竞争 2 号筷子。即五个哲学家都竞争偶数号筷子,获得后,再去竞争奇数号筷子,最后总会有一个哲学家能获得两支筷子而进餐。
而申请不到的哲学家进入阻塞等待队列,根据 FIFO 原则, 则先申请的哲学家会较先可以饭, 因此不会出现饿死的哲学家。
semaphore chopstick[5]={1, 1, 1, 1, 1};
void philosopher(int i){
while(true){
think();
if(i%2 == 0){
//偶数哲学家, 先右后左。
wait (chopstick[ i + 1 ] mod 5) ;
wait (chopstick[ i]) ;
eat();
signal (chopstick[ i + 1 ] mod 5) ;
signal (chopstick[ i]) ;
}
else{
//奇数哲学家, 先左后右。
wait (chopstick[ i]) ;
wait (chopstick[ i + 1 ] mod 5) ;
eat();
signal (chopstick[ i]) ;
signal (chopstick[ i + 1 ] mod 5) ;
}
}
}
参考: