管程試圖抽象相關並發進程對共享變量訪問,以提供一個友善的並發程序設計開發環境
管程是由若干公共變量及其說明和所有訪問這些變量的過程所組成
管程把分散在各個進程中互斥地訪問公共變量的那些臨界區集中起來管理,管程的局部變量只能由該管程的過程存取
進程只能互斥地調用管程中的過程
TYPE <管程名> = MONITOR <管程變量說明>; define <(能被其他模塊引用的)過程名列表>; use <(要引用的模塊外定義的)過程名列表>; procedure <過程名>(<形式參數表>); begin <過程體>; end; …… procedure <過程名>(<形式參數表>); begin <過程體>; end; begin <管程的局部數據初始化語句>; end;
條件變量(condition variables):當調用管程過程的進程無法運行時,用於阻塞進程的信號量
阻塞原語wait:當一個管程過程發現無法繼續時(如發現沒有可用資源時),它在某些條件變量上執行wait,這個動作引起調用進程阻塞
釋放原語signal:用於釋放在條件變量上阻塞的進程
如圖,管程過程必須被互斥調用,等待調用管程過程的進程隊列必須在互斥調用管程過程隊列排隊
每個條件變量都對應一個條件變量隊列。調用管程過程的進程執行不下去的時候,進入對應條件變量隊列等待,並開放管程。
當使用signal釋放一個等待進程時,可能出現兩個進程同時停留在管程內。解決方法:
(1)執行signal的進程等待,直到被釋放進程退出管程或等待另一個條件
(2)被釋放進程等待,直到執行signal的進程退出管程或等待另一個條件
被釋放的進程在互斥調用管程過程高優先級隊列,等待調用管程過程。
霍爾采用了第一種辦法;漢森選擇了兩者的折衷,規定管程過程所執行的signal操作是過程體的最后一個操作,但該方法增加了程序設計的難度
霍爾管程
霍爾管程基於PV操作原語實現:wait和signal可以是程序過程,不需要是原語
可以用語言機制(如通過操作系統程序庫或高級程序設計語言在基礎的操作系統的PV操作原語上)實現霍爾管程,而不需要擴展操作系統內核
用於管程中過程互斥調用的信號量mutex,初值為1
掛起發出signal()操作的進程的信號量next和計數器next_count
掛起等待資源的進程的信號量x_sem和計數器x_count(假設只有一個共享變量)
typedef struct InterfaceModule{ Semaphore mutex; Semaphore next; int next_count; } mutex = 1; next = 0; next_count = 0; procedure enter(InterfaceModule &IM) { P(IM.mutex); //互斥進入管程 } procedure leave(InterfaceModule &IM) { if(IM.next_count>0) { //開放管程時首先釋放互斥調用管程過程高優先級隊列里的進程,沒有才釋放低優先級隊列中的 IM.next_count--; V(IM.next); //釋放一個next上掛起的進程 } else V(IM.mutex);//否則向互斥調用管程過程隊列開放管程 } procedure wait(semaphore &x_sem , int &x_count , InterfaceModule &IM) { x_count++; //等待這個資源的進程數加1 if(IM.next_count>0) { //首先釋放高優先級隊列里的進程 IM.next_count--; V(IM.next); } else V(IM.next); P(x_sem); } procedure signal(semaphore &x_sem , int &x_count , InterfaceModule &IM) { if(x_count>0) { //判斷條件變量隊列中是否有等待資源的進程 x_count--; IM.next_count++; V(x_sem); //釋放一個等待資源的進程 P(IM.next); }
霍爾管程實現哲學家就餐問題:
當且僅當哲學家的兩個鄰座state[(i-1)%5]和state[(i+1)%5]均不為eating時才建立狀態state[i]=eating
引入條件變量semaphore self[5],當哲學家i飢餓但又不能獲得兩把叉子時,進入其信號量等待隊列
type dining philosophers = monitor { enum{think , hungry , eating} state[5]; cond self[5]; self[5] = 0; int self_count[5]; self count[5] = 0; InterfaceModule IM; for(int i=0; i<5; i++) state[i] = thinking; define pickup , pickdown; use enter , leave , wait , signal; } procedure pickup(int i) { enter(IM); state[i] = hungry; test(i); if(state[i]!=eating) wait(self[i] , self_count[i] , IM) leave(IM); } procedure putdown(int i) { enter(IM); state[i]=think; test((i-1)%5); test((i+1)%5); leave(IM); } procedure test(int k) { if((state[(k-1)%5]!=eating)&&(state[k]==hungry)&&(state[k+1]!=eating)) { state[k] = eating; signal(self[k] , self_count[k] , IM); } }
想吃時調用過程pickup,吃完調用過程putdown。
霍爾管程解決生產者-消費者問題:
type producer_consumer = monitor { item B[k]; int in , out; int count; cond notfull , notempty; notfull = notempty = 0; int notfull_count , notempty_count ; notefull_count = notempty_count = 0; InterfaceModule IM; define append , take; use enter , leave , wait , signal; } procedure append(item &x) { enter(IM); if(count==k) wait(notfull , notfull_count , IM); B[in] = x; in = (in+1)%k; count++; signal(notempty , notempty_count , IM); leave(IM); } procedure take(item &x) { enter(IM); if(count==0) wait(notempty , notempty_count , IM); x = B[out]; out = (out+1)%k; count—; signal(notfull , notfull_count , IM); leave(IM); } cobegin process producer_i() { item x; produce(x); producer_consumer.append(x); } process consumer_i() { item x; producer_consumer.take(x); consume(x); } coend
霍爾管程解決讀者-寫者問題:
TYPE read-writer = MONITOR var Rc, Wc : integer; R, W : semaphore; rc, wc: integer; define start_read, end_read, start_writer, end_writer; use wait, signal, check, release; begin rc:=0; wc:=0; Rc:=0; Wc:=0; R:=0; W:=0; end; procedure start_read; begin if wc>0 then wait(R,Rc,IM); rc := rc + 1; signal(R, Rc, IM); end; procedure end_read; begin rc := rc - 1; if rc=0 then signal(W,Wc,IM); end; procedure start_write; begin wc := wc + 1; if rc>0 or wc>1 then wait(W,Wc,IM); end; procedure end_write; begin wc := wc - 1; if wc>0 then signal(W,Wc,IM); else signal(R, Rc,IM); end;
