第二章 程序管理(3)
經典同步問題
訊號量題目做題一般方法:
1. 分析問題,找出同步、互斥關係
2. 根據資源設定訊號量變數
3. 寫出程式碼過程,並注意P、V操作的位置
4. 檢查程式碼,模擬機器執行,體驗訊號量的變化和程式執行過程是否正確。
1.生產者-消費者問題(互斥、同步)
無論生產者、消費者使用緩衝池時應保證互斥使用(互斥訊號量mutex )
生產者和消費者間交叉有序:
有序的控制最根源在產品數量上。
設定兩個訊號量:
分別針對生產者、消費者設定不同的訊號量,empty和full分別表示緩衝池中空緩衝池和滿緩衝池(即產品)的數量。
empty、full兩者有天然的數量關係,在PV控制下值不斷變化,但在值等於0的點上是控制順序的關鍵。
變數和訊號量
buffer: array [ 0, …, n-1] of item;
in, out: integer :=0, 0;
不使用Counter變數,而是用訊號量
Var mutex, empty, full: semaphore :=1, n, 0; producer : repeat … produce an item in nexp; … buffer(in):=nexp; in:=(in+1) mod n; until false; consumer : repeat nextc:=buffer(out); out:=(out+1) mod n; consume the item in nexc; until false;
檢查:
1. 每個程式中用於實現互斥的wait(mutex)和signal(mutex)必須成對地出現。
2. 控制順序的訊號量empty和full的wait和signal操作,成對地出現在不同的程序中。
3. 在每個程式中的多個wait操作順序不能顛倒。且應先執行對資源訊號量的wait操作,再執行對互斥訊號量的wait操作,否則可能引起程序死鎖。
4. 模擬交替執行過程,檢查控制是否正確。
模擬交叉執行
1)兩個生產者同時的情況
假設的時間片輪轉順序如下:
produce; produce; wait(e); wait(m); buffer(in):=nexp; wait(e); wait(m); buffer(in):=nexp; in:=(in+1)mod n; signal(m); signal(f); in:=(in+1)mod n; signal(m); signal(f);
1號生產者的wait操作使e=n-1,m=0
當1號正在in=0的buffer[0]放入產品時,2號來到,wait(e)仍通過,e=n-2;但wait(m)會使2號阻塞。m=-1,e=n-2。
轉入1號生產者執行使in=1,,喚醒m上的等待者——2號生產者,但可能並不立即切換到2號執行,而是其分到的cpu時間完成後再轉到2號。此時m=0,f=1。
回到2號程序後,將產品放入緩衝區;in=2;m=1;f=2。
2)生產者等待消費者情況
假設的時間片輪轉順序如下
produce;
wait(e);
wait(m);
buffer(in):=nexp;
wait(f);
wait(m);
nextc:=buffer(out);
in:=(in+1)mod n;
signal(m);
signal(f);
out:=(out+1) mod n;
signal(m);
signal(e);
consume;
設當前執行到一個臨界點,緩衝區滿了,而生產者先來了要生產。
e=0;m=1;f=n
注意:signal原語喚醒的因wait阻塞的程序後該程序並不需再重新執行wait
##AND訊號量的方式
consumer :
repeat
nextc:=buffer(out);
out:=(out+1) mod n;
consume the item in nexc;
until false;
2. 哲學家進餐問題(互斥、死鎖)
五個哲學家共用一張圓桌,分別坐在周圍的五張椅子上,在桌子上有五隻碗和五隻筷子,他們的生活方式是交替地進行思考和進餐。平時,一個哲學家進行思考,飢餓時便試圖取用其左右最靠近他的筷子,只有在他拿到兩隻筷子時才能進餐。進餐畢,放下筷子繼續思考。
可見:相鄰兩位不能同時進餐;最多隻能有兩人同時進餐。
1)記錄型訊號量解決哲學家進餐問題
筷子是臨界資源,在一段時間內只允許一個哲學家使用。為實現對筷子的互斥使用,用一個訊號量表示一隻筷子,五個訊號量構成訊號量陣列。
Var chopstick: array [0, …, 4] of semaphore;
所有訊號量均被初始化為1。
第i 位哲學家的活動可描述為:
repeat
wait(chopstick[ i ]);
wait(chopstick[ ( i +1) mod 5] );
…
eat;
…
signal(chopstick[ i ]);
signal(chopstick[ ( i +1) mod 5] );
…
think;
until false;
2) 就餐死鎖問題
假如五位哲學家同時飢餓而各自拿起左邊的筷子時,就會使五個訊號量chopstick均為0,當他們再試圖去拿右邊的筷子時,都將因無筷子可拿而無限等待。
解決方法:
->數量控制:
至多隻允許有四位哲學家同時去拿左邊的筷子,最終能保證至少有一位哲學家能夠進餐,並在用畢後釋放出他用過的兩隻筷子,從而使更多的哲學家能夠進餐。
—限制併發執行的程序數
->一刀切:
僅當哲學家的左右兩隻筷子均可用時,才允許他拿起筷子進餐。
—採用AND訊號量。
Var chopstick: array [0, …, 4] of semaphore:=(1, 1, 1, 1, 1);
Process i
repeat
think;
Swait(chopstick[ ( i +1) mod 5] , chopstick[ i ] );
eat;
Ssignal(chopstick[ ( i +1) mod 5] , chopstick[ i ] );
until false;
3. 讀者-寫者問題(有條件的互斥)
一個數據檔案被多個程序共享。Reader程序只要求讀檔案,Writer程序要求寫入內容。
合理的同步關係是:
->多個讀程序可同時讀;
->Writer程序與任何其他程序(包括Reader程序或其他Writer程序)不允許同時訪問檔案。
讀者Reader :
begin
repeat
wait(rmutex);
if Readcount=0 then wait(wmutex);
Readcount :=Readcount +1;
signal(rmutex);
…
perform read operation;
…
wait(rmutex);
Readcount :=Readcount -1;
if Readcount=0 then signal(wmutex);
signal(rmutex);
until false;
end
利用訊號量集機制解決讀者——寫者問題
• 引入訊號量L控制讀者的數目,初值為RN。
• 互斥訊號量mx,初值為1。
• 利用訊號量集的一種特殊情況——開關。
讀者:判斷L,判斷是否有寫者;
寫者:判斷是否有讀者;
reader :
repeat
Swait(L, 1, 1);
…
// 讀操作;
…
Ssignal(L, 1);
until false;
writer :
repeat
Swait(mx, 1, 1; L, RN, 0);
//寫操作;
Ssignal(mx, 1);
until false;