哲學家進餐問題:
(一)問題:
5個哲學家圍坐在一個圓桌上,每兩個哲學家之間都有一隻筷子,哲學家平時進行思考,只有當他們饑餓時,才拿起筷子吃飯。
規定每個哲學家只能先取其左邊筷子,然後取其右邊筷子,然後才可以吃飯。
(二)分析:
每一隻筷子都是一個臨界資源,設定5個互斥訊號量。
Semaphore stcik[5]={1,1,1,1,1}
因為:只有佔有左邊筷子-》佔有右邊筷子-》吃飯
所以p(左邊筷子)-》p(右邊筷子)-》吃飯
(三)實現:
main(){
philosopher(0);//注意不可以用迴圈,因為此中是4個並列的進程。
philosopher(1);
philosopher(2);
philosopher(3);
philosopher(4);
}
philosopher(int i){
while(1){
思考;
p(stick[i]);//取左邊筷子
p(stick[i+1]);//取右邊筷子
進餐;
V(stick[i]);
v(stick[i+1]);
}
}
(四)問題:
如果5個哲學家同時拿起自己左邊的筷子,就會發生死結。
(五)防止死結的方法:
(1)方法一:
規定在拿到左側的筷子後,先檢查右面的筷子是否可用。如果不可用,則先放下左側筷子, 等一段時間再重複整個過程。
問題:發生饑餓現象;
如果同時拿起左邊筷子,則右邊的筷子都不可用,則放下,然後再次拿起,。。。,則誰都無法就餐,
(2)方法二:
最多允許四個哲學家同時進餐,以保證至少有一個哲學家能夠進餐,最終總會釋
放出他所使用過的兩支筷子,從而可使更多的哲學家進餐。
偽碼:
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
}
}
(3)將拿左筷子,與拿右筷子當做一個原子操作:(即只有當左右筷子均可以拿到時,才拿筷子。)
方法一:
利用AND 型訊號量機制實現:根據課程講述,在一個原語中,將一段代碼同時需
要的多個臨界資源,要麼全部分配給它,要麼一個都不分配,因此不會出現死結的情形。
偽碼:
semaphore chopstick[5]={1,1,1,1,1};
void philosopher(int I)
{
while(true)
{
think();
P(chopstick[(I+1)]%5,chopstick[I]);
eat();
V(chopstick[(I+1)]%5,chopstick[I]);
}
}
方法二:
利用訊號量的保護機制實現。通過訊號量mutex對eat()之前的取左側和右側筷
子的操作進行保護,使之成為一個原子操作,這樣可以防止死結的出現。
偽碼:
semaphore mutex = 1 ;
semaphore chopstick[5]={1,1,1,1,1};
void philosopher(int I)
{
while(true)
{
think();
P(mutex);
P(chopstick[(I+1)]%5);
P(chopstick[I]);
V(mutex);
eat();
V(chopstick[(I+1)]%5);
V(chopstick[I]);
}
}
(4)規定奇數號的哲學家先拿起他左邊的筷子,然後再去拿他右邊的筷子;而偶數號 的哲學家則相反。
偽碼:
semaphore chopstick[5]={1,1,1,1,1};
void philosopher(int i)
{
while(true)
{
think();
if(i%2 == 0) //偶數哲學家,先右後左。
{
P (chopstick[ i + 1 ] mod 5) ;
P (chopstick[ i]) ;
eat();
V (chopstick[ i + 1 ] mod 5) ;
V (chopstick[ i]) ;
}
Else //奇數哲學家,先左後右。
{
p (chopstick[ i]) ;
p (chopstick[ i + 1 ] mod 5) ;
eat();
V (chopstick[ i]) ;
V (chopstick[ i + 1 ] mod 5) ;
}
}