轉載,原文地址http://www.cnblogs.com/phinecos/archive/2006/08/25/486552.html
1.整型信號量是一個整數變量,除初始化外,對其只能執行兩個操作,即wait(s)和signal(s),也叫p(s)和v(s)操作,均是原語操作,用來實現進程的同步,互斥.
2.記錄型信號量
結論:
(1) 若信號量s爲正值,則等於在封鎖進程之前對信號樑可以施行的p操作數,也就是s所代表的實際可以使用的物理資源數.
(2) 若信號量s爲負值,則其絕對值等於排列在該信號量s隊列之中等待的進程個數,也就等於對信號量s實施p操作而被封鎖起來並進入信號量s隊列的進程數.
(3)p操作一般代表請求一個資源,v操作一般代表着釋放一個資源,在一定條件下,p操作代表掛起進程操作,v操作代表喚醒被掛起的進程.
3.經典同步問題
1)生產者/消費者問題
a) 問題一:有一個生產者和一個消費者,共享一個緩衝區.兩者要互斥訪問緩衝區.
解:定義兩個信號量:empty表示緩衝是否爲空,初值爲1,即初始時可以存入一件物品.full表示緩衝區中是否有物品,初值爲0,即初始時緩衝區沒有物品.
注:
(1)進程互斥只需要一個信號量,而同步可能要兩個信號量.
(2)p,v操作仍然要成對,但在進程進入臨界區前後調用的是針對不同信號量的wait,signal操作,而進程互斥時是針對相同的信號量.
(3)至少有一個信號量的初值>=1,否則所有進程無法執行,一般是指管理是否允許訪問共享資源的那個信號量.如這裏的empty設爲1,如果緩衝區容量爲n,則可以設爲n;
b)問題二:m個生產者,n個消費者,容量爲r的緩衝區,(m,n,r都大於1),不要生產者和消費者互斥存取物品.
解:1)生產者和消費者之間要同步,類似問題一,用兩個信號量empty,full;2)m個生產者之間要互斥,n個消費者之間也要互斥,但生產者和消費者不用互斥存取物品,因此設兩個計數器in,out和相應的互斥信號量mutex1,mutex2
c)問題三:m個生產者,n個消費者,容量爲r的緩衝區,(m,n,r都大於1),要求生產者和消費者互斥存取物品.
解:1)生產者和消費者之間要同步,類似問題一,用兩個信號量empty,full;2)m個生產者之間要互斥,n個消費者之間也要互斥,同時要求生產者和消費者互斥存取物品,因此設兩個計數器in,out和一個互斥信號量mutex.
代碼:
注:應該先執行對資源信號量的p,v操作,再執行互斥信號量的p,v操作,否則會引起死鎖.
2)讀者/寫者問題
要求:(1)允許多個讀者同時從數據區讀數據;
(2)當讀者在讀數據時,不允許寫者寫數據
(3)任何時候只允許一個寫者向數據區寫數據;
(4)若有寫者在寫數據,則不允許讀者讀數據.
問題分爲讀者優先和寫者優先兩類。若有讀者在讀數據時,寫者到來時就會被阻塞,直到所有讀者讀完數據才被喚醒.這裏讀者優先於寫者,稱爲第一類讀寫問題.若讀者在讀數據時,有寫者到來,則後續的讀者將被阻塞,直到寫者離開才被喚醒,這類問題中寫者處於優勢地位,叫第二類讀寫問題.
a)問題一:用記錄型信號量解第一類讀寫問題
注:
(1)WRmutex用於讀者和寫者,寫者和寫者之間互斥訪問數據區.
(2)當沒有讀者訪問時,允許一個讀者進入,第一個讀者進入時要用WRmutex與寫者互斥.
(3) readcount記錄正在讀數據區的讀者數量,由於能被多個讀者進程共享,也是臨界資源,因此用Rmutex來對其實施互斥訪問.
(4)允許多個讀者同時讀數據,當至少有一個讀者在讀時,其他讀者不用等待就可以讀數據。
(5)一旦有一個讀者開始讀數據,其後,只要至少還有一個讀者在讀數據,讀者就一直保持對數據區的控制權,這會造成寫者的"飢餓";
b)問題二:用AND信號量解第一類讀寫問題
c)第二類讀寫問題
3)哲學家進餐問題
a)使用記錄型信號量
b)使用AND信號量
4.真題精選
[復旦2000]
問答: 在計算機科學文獻中的幾種互斥算法中,所謂的Lamport麪包店算法可以有效地用於多個相互競爭的控制線程,該算法中線程之間的通信只能在共享內存中進行(即,不需要諸如信號量、原子性的set-and-test之類的專門機制)。該算法的基本思想源於麪包店,因爲麪包店需要先取號然後等候叫號。下面給出了該算法的框架,該算法可以使各線程進出臨界區而不產生衝突。
定義(a,b)<(c,d)爲(a<c) or (a=c and b<d);
Enter[i]和Number[i]可以被進程i讀寫,但是隻能被進程j(j<>i)讀.
請問:(1)算法如何解決互斥訪問臨界區問題
(2) 算法如何解決死鎖問題
分析:每個進入麪包店的顧客會得到一個號碼,服務員爲擁有最小號碼的顧客服務.此算法需要進程pi經過兩個步驟進入臨界區.第一步,它需要選擇一個號碼,因此,它讀取其他進程的號碼,並選擇一個比它們的最大值大1的數字作爲自己的號碼(稱爲"麪包店入口).第二步,進程pi用如下方法檢查是否可以進入臨界區.對任意其他進程pj,pi首先檢查pj是否已經在麪包店入口,如果在,那麼pi等待pj離開麪包店入口,然後pi等待Number[j]變爲0或者Number[j],j) < (Number[i],i).當pi對所有其他進程成功驗證上述條件後,就可以進入臨界區.
解:
首先證明兩個結論:
(1)若進程pi已經在臨界區,而且若干其他進程pk已經選擇了他們的號碼,那麼(Number[i],i) < (Number[k],k).
證明:若pi已經在臨界區,則一定經歷了k輪for循環,就是說在上述幾輪循環中,Number[k]=0或者(Number[i],i) < (Number[k],k).首先假設進程pi讀出Number[k]爲0,也就是說pk還沒有選擇好一個號碼,這裏有兩種情形:一是pk不在麪包店入口,二是已經進入但還沒有退出.若pk不在麪包店入口,則它將可以讀到最新的Number[i]的值,從而確保Number[k]>Number[i].若它已經在入口處,則此次進入一定是在pi檢查了Enter[k]之後,因爲pi在檢查條件(Number[k]==0) && ((Number[i],i) < (Number[k],k))) 之前需要等待pk完成選擇,也就是說pk將讀到最新的Number[i]的值,因此確保Number[k]>Number[i].如果在k輪循環中,(Number[i],i) < (Number[k],k).那麼這個結論將繼續保持,因爲Number[i]的值不會改變,而Number[k]只會增加.
(2)若進程pi在臨界區中,則Number[i]>0
證明:因爲此數值至少爲0,當一個進程進入臨界區之前一定執行了一個加1的操作.
由上述結論,若有兩個進程pi和pk同時進入了臨界區,由(2)知它們的號碼都大於0,由(1),則有Number[k]>Number[i],反之也一樣,所以矛盾.
對於死鎖問題,由上述結論知,任一時刻一定存在一個進程pi,它的(Number[i],i)值最小,因此它總能夠執行完成並歸還資源,所以不會產生死鎖.