之前學了信號量機制的幾個問題:
生產者消費者問題: https://www.cnblogs.com/wkfvawl/p/11529681.html
多生產者消費者問題:https://www.cnblogs.com/wkfvawl/p/11531382.html
吸煙者問題:https://www.cnblogs.com/wkfvawl/p/11534452.html
讀者寫者問題: https://www.cnblogs.com/wkfvawl/p/11538431.html
這里再介紹幾個問題。
一、猴子過鐵索問題
1、問題描述
兩個山崖間有一根鐵索,山崖兩邊各有一群猴子,任何時候同時只能有一個方向的猴子通過鐵索。使用P、V操作寫出山崖兩邊的猴子過鐵索的算法。
2、問題分析
一個山上的猴子就是一群讀者,第二個山上的猴子為另一群讀者,兩群讀者互斥使用鐵索。
設信號量waymutex表示山兩邊的猴子對鐵索的互斥共享,初值為1;設m1count和m2count表示對兩邊猴子的記數,其初值為0;設m1mutex 和m2mutex表示兩群猴子中各猴子互斥訪問記數變量的信號量,初值都為1,其同步與互斥的算法如下:
semaphore waymutex=1; semaphore m1mutex=1, m2mutex=1; int m1count=0, m2count=0; Monkeygroup1() { while(1) { P(m1mutex); //第一群猴子之間互斥的訪問m1count if(m1count==0)//同一方向的該群猴子中的第一個負責“加鎖” { P(waymutex); } m1count=m1count+1;// V(m1mutex); 猴子通過鐵索; P(m1mutex); m1count=m1count-1; if(m1count==0)//同一方向的該群猴子中的最后一個個負責“解鎖” { V(waymutex); } V(m1mutex); } } Monkeygroup2() { while(1) { P(m2mutex);//第二群猴子之間互斥的訪問m1count if(m2count=0) { P(waymutex); } m2count=m2count+1; V(m2mutex); 猴子通過鐵索; P(m2mutex); m2count=m2count-1; if(m2count=0) { V(waymutex); } V(m2mutex); } }
3、總結
該問題是讀者寫者問題的變種,讀者寫者問題中只要求多個讀者可以同時對文件讀取,寫者不可以。這里的鐵索和文件是一樣的,只有這一份臨界資源,但兩邊的兩群猴子同時只能一個方向的猴子通過,也就相當於讀者寫者問題中,多個寫者也可以同時對文件進行操作。
該問題的另外一種描述更加貼近生活:
設A、B兩點之間是一段東西向的單行車道,現在要設計一個AB路段自動管理系統,管理規則如下:當AB間有車輛在行駛時同方向的車可以同時駛入AB段,但另一方向的車必須在AB段外等待;當AB段之間無車輛行駛時,到達AB段的任一方向的車都可進入AB段,但不能從兩個方向同時駛入,即只能有一個方向的車駛入;當某方向在AB段行駛的車輛駛出了AB段且暫無車輛進入AB段時,應讓另一方向等待的車輛進入AB段行駛。試用信號量和P、V操作管理AB路段車輛的行駛。
二、兩人下棋問題
1、問題描述
兩人下象棋的過程可以概括為:一開始只能是“紅先黑后”,以后兩人要循環輪流走子,直至某一方獲勝或雙方和棋為止。這是個只有一個生產者和一個消費者的生產者——消費者問題,是個典型的“你等我,我也等你”的問題。
2、問題分析
紅方是總的前趨任務——生產者進程,黑方是總的后繼任務——消費者進程,但由於下棋過程必須輪流走子,所以紅黑雙方的生產者消費者身份會輪流改變。棋盤則是生產者與消費者共享的緩沖。
所用信號量設置如下:
Ⅰ)同步信號量hei,初值為1,表示黑方已走子,開始時可使紅方先行不受阻。
Ⅱ)同步信號量hong,初值為0,表示紅方尚未走子, 開始時可使黑方先行受阻。
semaphore hei = 1; semaphore hong =0; player1()//紅方 { while(1) { P(hei); 若被黑方將死,則投子認負,結束; 若同意與黑方作和,則結束; 否則,根據棋局思考后走一子; V(hong); } } player2()//黑方 { while(1) { P(hong); 若被紅方將死,則投子認負,結束; 若同意與紅方作和,則結束; 否則,根據棋局思考后走一子; V(hei); } }
3、總結
該問題是消費者生產者問題的變種,不同於生產者消費者問題,該問題中只能有一個紅方進程和一個黑方進程,也只有一種同步關系(生產者消費者問題中還有對緩沖池這種臨界資源的互斥訪問)。同時我們需要注意在下棋的時候總是紅方先行,
在“前操作”之后執行V(S)
在“后操作”之前執行P(S)
三、醫生與化驗室問題
1、問題描述
醫生的看病活動:要病人去化驗->等待化驗結果->繼續看病
化驗室活動:等待化驗單,進行化驗,開出化驗結果
2、問題分析
(1)同步關系:化驗室必須等到有化驗單才能開始化驗。醫生必須等到有化驗結果才能繼續看病。
(2)設置信號燈Sa:表示是否有化驗單,初值為0
設置信號燈Sb:表示是否有化驗結果,初值為0。
semaphore sa=0; //是否有化驗單 semaphore sb=0; //是否有化驗結果 Diagnosis()//醫生進程 { while (看病工作未完成) { 看病; V(Sa); //開化驗單 P(Sb); //等化驗結果 看病; } } Labora()//化驗室進程 { while (化驗工作未完成) { P(Sa); //等化驗單 化驗; V(Sb); //開出化驗結果 } }
3、總結
該問題與前面的紅黑雙方下象棋的例子類似但不同。我們去醫生那里看病,醫生先讓我們去化驗室化驗結果,得到化驗結果后才能看病,也就是一個醫生->化驗室->醫生的同步關系。上面的紅黑雙方下棋是紅方進程下完棋后黑方進程下棋,黑方進程下完棋后紅方進程下棋,但這個問題是在醫生進程還沒有看完病結束的情況下,讓化驗室進程化驗開出化驗結果后,醫生進程才可以根據化驗單診斷看病,結束進程。
四、超市購物問題
1、問題描述
某小型超級市場,可容納50人同時購物。入口處有籃子,每個購物者可拿一只籃子入內購物。出口處結帳,並歸還籃子(出、入口禁止多人同時通過)。試用信號量和P、V操作寫出購物者的同步算法。
2、問題分析
1)互斥關系:a最多只能有50個人同時購物,b顧客對籃子的使用是互斥的。
2)所用信號量設置如下:
Ⅰ)互斥信號量S,初值為50,用以保證最多可以有50個購物者同時進入超市。
Ⅱ)互斥信號量mutex,初值為1,用以保證同時只能有一個購物者進程進入出入口拿起籃子或者結帳后放下籃子。
3)用信號量機制給出的每個購物者購物過程的算法描述如下:
semaphore s = 50; semaphore mutex =1; customer() { while(1) { P(S); P(mutex); 從入口處進超市,並取一只籃子; V(mutex); 進超市內選購商品; P(mutex); 到出口結帳,並歸還籃子; V(mutex); 從出口離開超市; V(S); } }
3、拓展
有一個閱覽室,共有100個座位,讀者進入時必須先在一張登記表上登記,該表為每一座位列一表目,包括座號和讀者姓名等,讀者離開時要消掉登記的信息,試問:
(1) 為描述讀者的動作,應編寫幾個程序,設置幾個進程?
(2) 試用PV操作描述讀者進程之間的同步關系。
讀者的動作都是一樣的:登記進入閱覽室,閱讀,撤消登記離開閱覽室,因此可寫一個程序,設n(n≥100)個進程。
讀者共享的資源有閱覽室的座位和登記表,因此諸個讀者進程之間有兩種互斥制約關系,需設2個信號量來實現:
seat:用於實現諸讀者對閱覽室的空閑座位的互斥競爭,初值為100;
mutex:用於實現諸讀者對登記表的互斥訪問,初值為1。
下面給出一種解法,當然還有其他解法(比如借鑒經典的讀者寫者問題的解法,利用讀者計數器變量)。
semaphore seat = 100; semaphore mutex =1; Reader() { P(seat); /*申請空座位*/ P(mutex); /*申請登記*/ 登記; V(mutex); /*允許其他讀者登記*/ 閱讀; P(mutex); /*申請撤消登記*/ 撤消登記; V(mutex); /*允許其他讀者撤消登記*/ V(seat); /*釋放座位,允許他人進入*/ }
五、理發師睡覺問題
1、問題描述
理發店里有一位理發師,一把理發椅和N把供等候理發的顧客坐的椅子。
如果沒有顧客,則理發師便在理發椅上睡覺。當一個顧客到來時,他必須先喚醒理發師。
如果顧客到來時理發師正在理發,則如果有空椅子,可坐下來等;否則離開。
2、問題分析
1)同步關系:只要有顧客存在,理發師就不能去睡覺,要去工作。
2)互斥關系:最多有N個顧客,理發師同一時刻只能給一位顧客理發。
2)信號燈設置:Customer:是否有顧客 mutex互斥信號燈 共享變量empty描述空椅子數
semaphore Customer = 0;//同步信號量.是否有顧客 semaphore mutex =1; //實現對empty變量的互斥訪問 int empty = N; barber() { while(1) { P(Customer); P(mutex); empty++; V(mutex); 理發; } } customer() { while(1) { P(mutex); if(empty>0)//只有還有空椅子的時候才會解鎖 { empty--; V(Customer); 等待理發; } V(mutex); } }
3、總結
該問題可以看成消費者生產者問題的一個變種。可以將理發店的顧客看成生產者,生產需要理發的人數這種資源,理發師看做消費者,給顧客理發,消費這種資源。
與生產者消費者問題不同的是,這里同時有多個生產者進程,但只有一個消費者進程,並且顧客和理發師是同步關系。
六、浴室問題
1、問題描述
一棟學生公寓里,只有一間浴室,且每次僅能容納一人。公寓里有男生也有女生。因此制定如下規定:
(1)每次只能有一個人在使用浴室;
(2)女生的優先級要高於男生;
(3)對於同性別的人來說,采用先來先服務的原則。
這道題被我們老師改成了使用電話亭.....不用浴室舉例,難道是有傷風化?
2、問題分析
下面給出4個函數,要求用信號量和PV原語實現這4個函數,來模擬上述規定下浴室里的過程。(假設初始時浴室為空)
(1)男生想要使用浴室: boy_wants_to_use_bathroom;
(2)男生離開浴室: boy_leaves_bathroom;
(3)女生想要使用浴室: girl_wants_to_use_bathroom;
(4)男生離開浴室: girl_leaves_bathroom;
按照個人理解,boy_wants_to_use_bathroom和girl_wants_to_use_bathroom是隨機出現的
而boy_leaves_bathroom和girl_leaves_bathroom是離開浴室后也就是使用完這種臨界資源后自動執行的。
定義信號量和其他變量:
int boy_waiting = 0;//正在等待的男生數 int girl_waiting = 0;//正在等待的女生數 int using = 0; //當前是否有人在使用浴室 Semaphore S_mutex = 1;//互斥信號量 Semaphore S_boys = 0;//男生等待隊列 Semaphore S_girls = 0;//女生等待隊列
男生想要使用浴室
void boy_wants_to_use_bathroom() { P(S_mutex); // 互斥訪問 if ((using==0) && (girl_waiting==0)) // 如果浴室空且沒有女生在等待 { using = 1; // 使用浴室洗澡 洗澡; V(S_mutex); // 洗完澡把浴室資源釋放 } else { boy_waiting++; // 男生等待隊列加長 V(S_mutex); // 釋放資源 P(S_boys); // 如果輪不到男生上手,阻塞:排隊等着 } }
男生離開浴室
void boy_leaves_bathroom() { P(S_mutex); // 互斥訪問 if (girl_waiting>0) // 先考慮女生 { girl_waiting--; // 女生等待隊列減少 V(S_girls); // 喚醒女生想要使用浴室進程 } else if (boy_waiting>0) // 再考慮男生 { boy_waiting--; // 男生等待隊列減少 V(S_boys); // 喚醒男生想要使用浴室進程 } else { using = 0; // 如果沒有人等待了,就把顯示浴室沒有人用 } V(S_mutex); // 釋放資源 }
女生想要使用浴室
void girl_wants_to_use_bathroom() { P(S_mutex); // 互斥訪問 if (using==0) // 如果浴室沒有人 { using = 1; // 使用浴室洗澡 洗澡; V(S_mutex); // 洗完澡把浴室資源釋放 } else { girl_waiting++; // 女生等待隊列增加 V(S_mutex); // 釋放資源 P(S_girls); // 如果輪不到女生上手,阻塞:排隊等着 } }
女生離開浴室
void girl_leaves_bathroom() { boy_leaves_bathroom(); // 和男生離開浴室一樣 }
3、總結
這應該是到目前為止最難的一道PV操作題了,我最開始想不明白那個using=0是什么時候設置的,后來想了想信號量的數據結構就明白了,那些在等待的進程都被掛在信號量那個隊列上了,也就是說上一個進程執行完,操作系統自動的從隊列中取隊首的那個進程。而需要設置using=0是隊列中沒有任何進程了,也就是說using=1是最開始的第一個使用浴室的進程設置的,而using=0是最后一個離開浴室的進程設置的。