生產者消費者問題
問題
一組生產者向一組消費者提供產品,它們共享一個有界緩衝區,生產者向其中投放產品,消費者從中取得產品。
規則
對於生產者:生產者生產一個產品(數據),正常放入倉庫(緩衝區)。若有人正在訪問倉庫(緩衝區),則需要等待;若無則訪問。若倉庫(緩衝區)已滿,則等待,等待一個消費者消費一個產品(數據),讓倉庫(緩衝區)不滿,再喚醒;若不滿則可直接放入。
對於消費者:消費者消費一個產品(數據),要去倉庫(緩衝區)去取。若有人正在訪問倉庫(緩衝區),則等待。若倉庫(緩衝區)爲空,則等待一個生產者來生產產品(數據)供消費者消費;若不爲空則取一個產品(數據)進行消費。
緩衝區是個臨界資源,諸進程對緩衝區的操作程序是一個共享臨界區,所以,還有個互斥的問題。
設置兩個同步信號量及一互斥信號量
Empty:說明空緩衝單元的數目,其初值爲有界緩衝區的大小n。即倉庫有多少空位。
Full: 說明滿緩衝單元的數目(即產品數目),其初值爲0。即倉庫已經有多少產品(有多少個位置被佔)
Mutex: 說明該有界緩衝區是一臨界資源,必須互斥使用,其初值爲1,保證任何時候只有一個進程使用緩衝區。
Int in=0;out=0;
Item buffer[n];
Semaphore mutex=1,empty=n,full=0;
//生產者
void procedure() {
do{
produce an item in nextp;
…
wait(empty);//檢驗是否滿了,若empty<0則阻塞 不執行下面的代碼
wait(mutex);//表示正在使用緩衝區
buffer(in)=nextp;
in=(in+1)%n;
signal(mutex);//釋放緩衝區
signal(full);//將產品數+1 做到進程同步
}while(TRUE);
}
//消費者
Void consumer(){
do{
wait(full);//檢查是否爲空,若full<0則說明緩衝區沒有產品消費了 阻塞進程
wait(mutex);//表示正在使用緩衝區
nextc=buffer(out);
out=(out+1)%n;
signal(mutex);//釋放緩衝區
signal(empty);//空位+1 做到進程同步
consumer the item in nextc;
…
}while (TRUE);
}
Void main() {
cobegin
procedure();
consumer();
coend;
}
注:
兩個wait不可以互換位置,因爲若互換,假設生產者進程執行,佔用了臨界資源mutex,而此時發現臨界區滿了(empty<0),便將自己阻塞而並未釋放mutex;此時消費者資源過來,發現臨界資源mutex(臨界區)正在被人使用,便也阻塞,從而造成死鎖。
兩個signal同樣不可以互換位置,同理。
哲學家進餐問題
問題
五個哲學家共用一張圓桌,分別坐在周圍的五張椅子上,在圓桌上有五碗通心粉和五根筷子,他們的生活方式是交替地進行思考和進餐。平時,哲學家進行思考,飢餓時便試圖取用其左右兩邊最靠近他的筷子,只有在他拿到兩根筷子時才能進餐。進餐後,放下筷子繼續思考。
分析
兩位哲學家不能擡起一根筷子喫飯,所以筷子是臨界資源,一次只能在一個人的手裏。
解決方法:用一個記錄型信號量來控制一根筷子的互斥使用,由這5個信號量構成信號量數組。其描述如下:
var chopstick: array [0,…,4] of semaphore;
讀者寫者問題
問題
允許多個讀進程同時讀一個共享對象,因爲讀操作不會使數據文件混亂。但不允許一個Writer進程和其他Reader進程或Writer進程同時訪問共享對象。因爲這種訪問將會引起混亂。
記錄型信號量
爲實現Reader和Writer進程間在讀或寫時的互斥而設置了一個互斥信號量Wmutex。另外,再設置一個整型變量readcount表示正在讀的進程數目。
semaphore rmutex=1,wmutex=1;
int readcount=0;
void reader(){
do {
wait(rmutex);
if (readcount=0) wait(wmutex);
readcount++;
signal(rmutex);
…
perform read operation;
…
wait(rmutex);
readcount--;
if (readcount=0) signal(wmutex);
signal(rmutex);
}while(TRUE);
}
void writer() {
do {
wait(wmutex);
perform write operation;
signal(wmutex);
} while(TRUE);
}
Void main() {
cobegin
reader();writer();
coend;
}
只要有一個Reader進程在讀,便不允許Writer進程去寫。因此,僅當readcount=0,表示尚無Reader進程在讀時,Reader進程才需要執行wait(wmutex)操作;若wait(wmutex)操作成功,Reader進程便可去讀,相應地,做readcount+1操作。
同理,僅當Reader進程在執行了readcount減1操作後其值爲0時,才須執行signal(wmutex)操作,以便讓Writer進程寫。
Readcount是一個可被多個Reader進程訪問的臨界資源,因此,應該爲它設置一個互斥信號量rmutex。
信號量集
與前面的讀者-寫者問題不同,增加了一個限制,即最多隻允許RN個讀者同時讀。爲此,又引入了一個信號量L,並賦予其初值爲RN,通過執行wait(L,1,1)操作,來控制讀者的數目,每當有一個讀者進入時,就要先執行wait(L,1,1)操作,使L的值減1。當有RN個讀者進入讀後,L便減爲0,第RN+1個讀者要進入讀時,必然會因wait(L,1,1)操作失敗而阻塞。
int RN;
semaphore L=RN,mx=1;
Void reader() {
do {
Swait(L,1,1);
//Swait(mx,1,0)語句起着開關的作用。只要無writer進程進入寫,mx=1,reader進程就都可以進入讀;一旦有writer進程進入寫時,其mx=0,則任何reader進程就都無法進入讀;
Swait(mx,1,0);
…
perform read operation;
…
Ssignal(L,1);
}while(TRUE);
}
void writer() {
do {
//Swait(mx,1,1,L,RN,0)語句表示僅當既無writer進程在寫(mx=1),又無reader進程在讀(L=RN)時,writer進程才能進入臨界區寫。
Swait(mx,1,1;L,RN,0);
perform write operation;
Ssignal(mx,1);
}while(TRUE);
}
Void main() {
cobegin
reader();writer();
coend;
}
總結了快一個下午…好累