1. 程式人生 > >程序同步問題之哲學家進餐

程序同步問題之哲學家進餐

哲學家進餐問題:
由 Dijkstra 提出並解決的哲學家進餐問題(The Dinning Philosophers Problem)是典型的同步問題。該問題是描述有五個哲學家共用一張圓桌,分別坐在周圍的五張椅子上,在圓桌上有五個碗和五隻筷子,他們的生活方式是交替地進行思考和進餐。平時,一個哲學家進行思考,飢餓時便試圖取用其左右最靠近他的筷子,只有在他拿到兩隻筷子時才能進餐。進餐完畢,放下筷子繼續思考。

利用記錄型訊號量解決哲學家進餐問題:
經分析可知,放在桌子上的筷子是臨界資源,在一段時間內只允許一位哲學家使用。為了實現對筷子的互斥使用,可以用一個訊號量表示一支筷子,由這五個訊號量構成訊號量陣列。其描述如下:

Var chopstick: array[0,…,4] of semaphor;
1
所有訊號量均被初始化為 1(說明筷子是臨界資源),5位哲學家都是平等的,故考慮第 i 位哲學家的活動可描述為:

repeat
wait(chopstick[i]); //申請拿起左邊的筷子
wait(chopstick[(i+1)mod 5]); //申請拿去右邊的筷子

eat; //吃飯

signal(chopstick[i]); //吃完飯後釋放左邊的筷子
signal(chopstick[(i+1)mod 5]); //吃完飯後釋放右邊的筷子

think; //思考
until false;
1
2
3
4
5
6
7
8
9
10
11
在以上描述中,當哲學家飢餓時,總是先去拿他左邊的筷子,即執行 wait(chopstick[i]);成功後,再去拿他右邊的筷子,即執行 wait(chopstick[(i+1)mod 5]);又成功後便可進餐。進餐完畢,又先放下他左邊的筷子,然後再放右邊的筷子。

這裡wait()操作與signal()操作的順序都可以顛到,因為左邊的筷子與右邊的筷子是平等的。但是一旦確定了一個哲學家拿筷子的順序,那麼5個哲學家的拿筷子的順序都要一樣。

但該解法有可能引起死鎖。假如五位哲學家同時飢餓而各自拿起左邊的筷子時,就會使五個訊號量 chopstick 均為 0; 當他們再試圖去拿右邊的筷子時,都將因無筷子可拿而無限期地等待。

對於這樣的死鎖問題,可採取以下幾種解決方法:

至多隻允許有四位哲學家同時去拿左邊的筷子,最終能保證至少有一位哲學家能夠進餐,並在用畢時能釋放出他用過的兩隻筷子,從而使更多的哲學家能夠進餐。
僅當哲學家的左、右兩隻筷子均可用時,才允許他拿起筷子進餐。
規定奇數號哲學家先拿他左邊的筷子,然後再去拿右邊的筷子,而偶數號哲學家則相反。按此規定,將是 1、2 號哲學家競爭 1 號筷子;3、4 號哲學家競爭 3 號筷子。即五位哲學家都先競爭奇數號筷子,獲得後,再去競爭偶數號筷子,最後總會有一位哲學家能獲得兩隻筷子而進餐。
關於程式中涉及到的wait()操作與signal()操作可以參考前一篇部落格:作業系統學習-6. 訊號量。

利用 AND 訊號量機制解決哲學家進餐問題:
在哲學家進餐問題中,要求每個哲學家先獲得兩個臨界資源(筷子)後方能進餐,這在本質上就是前面所介紹的 AND 同步問題,AND訊號量機制簡單來說就是“寧可錦上添花,也不雪中送炭”“把資源優先集中提供給一個程序”。故用 AND 訊號量機制可獲得最簡潔的解法。描述如下:

Var chopsiick array of semaphore:=(1,1,1,1,1);
processi
repeat
think;
Sswait(chopstick[(i+1)mod 5],chopstick[i]); //一次性分配兩個筷子
eat;
Ssignat(chopstick[(i+1)mod 5],chopstick[i]); //一次性釋放兩個筷子
until false;
1
2
3
4
5
6
7
8
AND訊號量通過原語操作來實現同時申請資源並且同時一次性釋放所佔用的資源。