1. 程式人生 > >進程同步概念簡介 多線程上篇(四)

進程同步概念簡介 多線程上篇(四)

target 事件 hog 場景 相關 img 一個數 行操作 申請

進程同步概念

臨界資源

一旦有對資源的共享,就必然涉及競爭限制 比如盡管有兩個人去水井打水,但是水井卻只有一個;合理安排的話剛好錯開,但是如果安排不合理,那就會出現沖突,出現沖突怎麽辦?總有一個先來後到,等下就好了。 這個水井就是一個臨界資源 臨界資源用來表示一種公共資源或者說是共享數據,可以被多個線程使用。 但是每一次,只能有一個線程使用它,一旦臨界資源被占用,其他線程要想使用這個資源,就必須等待。 當多進程訪問臨界資源時,比如打印機 假設A進程和B進程輪流獲得CPU時間片執行,A打印數學,B打印英語,如果不進行任何的控制與限制,可能會打印出來一張紙上一道數學計算題,接下來是一段英語的情況。
所以盡管A進程和B進程輪流獲得時間片運行,但是當需要訪問臨界資源時,一旦有一個進程已經開始使用,另外的進程就不能進行使用,只能等待。 計算機就是那個計算機,硬盤就是那個硬盤,一段代碼中的某個變量(共享變量)就是那個變量.....所有的一切都是只有一份,如果對於某個點多進程同時訪問,必然要做一定的限制 進程同步的主要任務是對多個相關進程在執行次序上進行協調,以使並發執行的諸進程之間能有效地共享資源和相互合作,從而使程序的執行具有可再現性

兩種制約關系

既然資源訪問有限制,到底有哪些場景是需要同步處理的?也就是何時會出現資源沖突? 技術分享圖片 看得出來,其實同步要解決的問題根本就是競爭,間接關系是赤裸裸的的競爭,共享同一個I/O就是一種競爭,盡管他們看似好像沒有什麽關系
直接的制約關系,源於進程間的合作,某種程度上來說也是一種競爭,只不過是有條件的競爭,他們共享緩沖區,當緩沖區滿時只能是消費者可以運行,生產者需要阻塞,這可以認為緩沖區滿這種情況下,消費者獨占了緩沖區,生產者不能使用了,不過這種情況下還是說成合作比較容易理解 所以,要麽是因為共享資源帶來的競爭,要麽就是相互合作帶來的依賴。

臨界區

有了臨界資源的概念,就很容易理解臨界區的概念,在程序中,所有的操作都是通過代碼執行的,訪問臨界資源的那段代碼就是臨界區 以打水為例,所以在還沒到井口,就要畫一個大圈,不允許第二個人進入範圍,“請站在安全黃線內”這句話熟悉麽?
這就是臨界區。 技術分享圖片

同步規則

如何才能夠合理處理競爭或者合作依賴導致的制約?
  • 空閑讓進
  • 忙則等待
  • 有限等待
  • 讓權等待
空閑讓進和忙則等待很好理解,對於臨界資源,如果空閑沒有被使用,誰來了之後都可以使用;如果臨界資源正在被使用,那麽其他後來者就需要進行等待 有限等待是指,要求訪問臨界資源的進程,應保證有限時間內能進入自己的臨界區,自己不能傻傻的等,傻傻等受傷的是自己 讓權等待是指,如果無法進入自己的臨界區時,應立即釋放處理機,而不能占著CPU死等,你死等就算了,別人卻也不能用了 有限等待和讓權等待是兩個維度 你不能為了一件事情不顧一切代價等個天荒地老,太傷身了; 如果你非要花五塊錢去蘇寧買一臺電視(等待事件發生),人家不賣給你(無法進入臨界區),你就賴著不走(忙等),你就耽誤別人做生意了(別的進程無法獲得CPU) 有限等待和讓權等待的共同特性是必須保證有條件的退出以給其他進程提供運行的機會 簡單說就是有限時間內你就要走開,你得不到更要走開,你即使能得到但是時間太久也得先讓一下別人 技術分享圖片 臨界區的設置就是安全黃線的設置,同步規則其實就是臨界區兩條黃線進出規則 對於臨界區,還可以進一步細分出來進入區和退出區以及剩余區 技術分享圖片

臨界區算法

Peterson算法 所以臨界區方式解決同步問題就是借助於算法,合理的控制對於臨界區的進入與退出,並且保障能夠做到:空閑讓進、忙則等待、有限等待、讓權等待 一種有名的算法為 Peterson Peterson算法適用於兩個進程在臨界區與剩余區間交替執行。假設兩個進程分別為p0 和 p1 為了表示方便,使用pi表示其中一個進程時,pj表示另外一個,顯然有i = 1-j(j = 1-i) 技術分享圖片 使用一個int類型變量turn 表示可以進入臨界區的線程,如果turn == i,表示pi可以進入臨界區 使用boolean 類型數組flag,總共兩個進程,所以flag[2],用來表示哪個進程想要進入臨界區,如果flag[i] = true;表示進程pi想要進入臨界區 上圖紅框內為進入區,藍框內為退出區 為了進入臨界區,進程pi首先設置flag[i]為true;並且設置turn為j; 顯然,根據while的條件,只有flag[j] == false 或者turn == i 時,pi可以進入臨界區 也就是如果我想進入的話,當對方不想進入或者當前允許我進入時,我就可以進入臨界區 顯然,如果只有一個進程想要進入,那麽如上所述,對方不想進入時,可以進入臨界區,符合空閑讓進 如果兩個進程都想進入,不管經過多麽激烈的競爭,當執行到while時flag[0] 和 flag[1] 都是true,也就是while內部的兩個條件,條件1始終是true 但是turn只會有一個值,要麽0 要麽1,也就是說while的第2個條件決定了誰會被while阻塞,誰能夠繼續執行下去 這種情況下必然能夠有一個進程會進入臨界區,另外一個被while循環阻塞,所以符合空閑讓進、忙則等待 當臨界區內的進程執行結束後,會設置flag[] 標誌位,如果此時另外的進程在等待,一旦設置後,其他進程就可以進入臨界區(剛才已經說了,如果pi想進入,flag[j] == false 或者turn == i 時可以進入)也就是說當前進程結束後,下一個進程就能夠進入了,所以滿足有限等待 小結: 上面的算法,滿足了通過進入區和退出區代碼的設置,可以做到同步的規則 如果只有一個想要進入臨界區,可以直接進入,如果兩個競爭進入,只有一個能夠進入,另一個會被while循環阻塞 Peterson只是一種臨界區算法,還有其他的

同步方式之信號量

1965年,荷蘭學者Dijkstra 提出的信號量(Semaphores)機制是一種卓有成效的進程同步工具。 臨界區算法的原理可以讓多進程對於臨界區資源的訪問串行化; 信號量機制允許多個線程在同一時刻訪問同一資源,但是需要限制在同一時刻訪問此資源的最大線程數目。

整型信號量

最初信號量機制被稱之為整型信號量 最初由Dijkstra 把整型信號量定義為一個用於表示資源數目的整型量 S,它與一般整型量不同,除初始化外,僅能通過兩個標準的原子操作(Atomic Operation) wait(S)和 signal(S)來訪問。 這兩個操作一直被分別稱為P、V操作(據說,據說因為Dijkstra是荷蘭人。在最初論文以及大多數文獻中,用P代表wait。用V代表signal。是荷蘭語中高度 proberen 和增量verhogen) Wait(S)和 signal(S)操作可描述為:
   wait(S):  while (S<=0); 
                  S:=S-1;
  signal(S):S:=S+1; 
wait表示資源申請:如果S小於等於0(資源不足)等待,如果滿足那麽將會進行S-1,也就是申請資源 signal表示資源釋放:每釋放一次資源,資源數目 S+1 P、V操作也稱之為操作原語,就是指原子操作 原子性是指一個操作是不可中斷的,要麽全部執行成功要麽全部執行失敗(具體怎麽保證的,此處可以不用關註) 技術分享圖片 多個線程並發的對資源進行訪問時,借助於PV原語操作,可以有效地做到共享資源的限制訪問。 但是,對於整型信號量,P操作也就是 wait(S)
wait(S):  while (S<=0);
                 S:=S-1;
如果獲取不到資源,將會持續while (S<=0);,將會永遠等待,進程處於忙等狀態 關於原語 wait(S)和 signal(S)這一原子操作叫做原語,原語是操作系統概念的術語,是由若幹條指令組成的,用於完成一定功能的一個過程 是由若幹個機器指令構成的完成某種特定功能的一段程序,具有不可分割性,即原語的執行必須是連續的,在執行過程中不允許被中斷。 原語是操作系統的核心部分組成,原語有不可中斷性。它必須在管態(內核態,系統態)下執行,並且常駐內存,而個別系統有一部分不在管態下運行。 可以簡單的理解是具有指定功能的操作系統提供的一個API性質的一種東西

記錄型信號量

鑒於整型信號量機制中的“忙等”情況,演化出來記錄型信號量 如果進程無法進入臨界區,那麽進入等待釋放CPU資源,並且通過一個鏈表記錄等待的進程。 記錄型信號量機制在整形信號量機制的基礎上增加了進程鏈表指針L,這也是記錄型信號量名稱的由來 記錄型信號量 semaphore 的結構如下:
semaphore {
value:int value;
L:進程等待鏈表(集合);
}
value相當於整型信號量中的S,L就是一個鏈表(集合) 簡言之,將整形信號量中的整型S,演化為一個結構,這個結構包括一個整型值,還有一個等待的進程鏈表 技術分享圖片 相對應整型信號量中的wait(S) 和 signal(S)可以描述為:
wait(S):
var S = semaphore;
S.value=S.value-1if S.value<0 then block(S.L); 

signal(S):
var S = semaphore;
S.value=S.value+1if S.value<=0 then wakeup(S.L);  
上面的操作中,均定義了一個semaphore類型的變量S
  • 如果執行 wait 操作,先執行資源減一,如果此時S.value<0,說明在申請資源之前(S.value-1),原來的資源就是<=0,那麽該進程阻塞,加入等待隊列L中
  • 如果執行 signal 操作,先執行資源加一,如果此時S.value<=0,說明在釋放資源之前(),原來的資源是<0的,那麽將等待鏈表中的進程喚醒
上面邏輯的關鍵之處在於:
  • 當申請資源時,先進行S.value-1,一旦資源出現負數,說明需要等待,S.value的絕對值就是等待進程的個數,也就是S.L的長度
  • 當資源恢復時,先進行S.value+1,已經有人釋放資源了然而資源個數還是小於等於0,說明原來就有人在等待,所以應該去喚醒
block 和 wakeup 也都是原語,也就是原子操作。 block原語,進行自我阻塞,放棄處理機,並插入到信號量鏈表S.L 中 wakeup原語,將S.L鏈表中的等待進程喚醒 如果 S.value的初值為 1,表示只允許一個進程訪問臨界資源,此時的信號量轉化為互斥信號量,用於進程互斥。(效果就如同Peterson算法了)

AND 型信號量

針對於臨界區算法或者是整型信號量或者是記錄型信號量是針對各進程之間只共享一個臨界資源而言的。 但是有些場景下,一個進程需要先獲得兩個或更多的共享資源後方能執行其任務。 假設A,B兩個進程,均需要申請資源D,E
process A: process B: wait(D); wait(E); wait(E); wait(D);
假設交替執行順序如下
process A: wait(D); 於是D=0 process B: wait(E); 於是E=0 process A: wait(E); 於是E=-1 A阻塞 process B: wait(D); 於是D=-1 B阻塞
最終A,B都被阻塞,如果沒有外力作用下,兩者都無法從阻塞狀態釋放出來,這就是死鎖

相關概念

對於一個水井,你在打水,另外的人就要等一等,這是人類大腦意識做出來的很基本的反應(人眼識別,大腦解析並且做出反應) 但是計算機程序並沒有這麽智能,你需要對他進行某些處理,以限制別的線程的訪問,比如你可以將“安全黃線”變成一個安全門,比如廁所,進去了之後把門關上。。。 這種概念就是鎖,鎖就是對資源施加控制,鎖指的是一種控制權 當進入臨界區時,我們稱之為獲得鎖,獲得鎖之後就可以訪問臨界資源; 其他線程想要進入臨界區,也需要先獲得鎖,顯然,他們獲取不到,因為此時,鎖被當前正在執行的線程持有 當前線程結束後,將會釋放鎖,別得線程就可以獲取這個資源的鎖,然後.... 死鎖 鎖表示一種控制權,對臨界資源的訪問權限,如果臨界資源不止一個,就可能出現這種情況: 需要先後訪問兩種臨界資源A和B,thread1獲得了A線程的鎖之後,等待獲得B的鎖,但是thread2獲得了資源B的鎖,在等待A資源的鎖,這就出現了互相等待的情況 比如一條窄橋,同一時刻僅僅允許一輛車通過,如果一旦出現兩輛車都走到橋的一半處,而且互不相讓,怎麽辦?這就是死鎖 解決方案 AND型信號量機制就是用於解決這種多共享資源下的同步問題的 AND 同步機制的基本思想: 將進程在整個運行過程中需要的所有資源,一次性全部地分配給進程,待進程使用完後再一起釋放。 只要尚有一個資源未能分配給進程,其它所有可能為之分配的資源也不分配給它。 也就是對若幹個臨界資源的分配,采取原子操作方式:要麽把它所請求的資源全部分配到進程,要麽一個也不分配。 這種思維就是通過對“若幹個臨界資源“的原子分配,邏輯上就相當於一份共享資源,要麽得到,也麽得不到,所以就不會出現死鎖 在 wait 操作中,增加了一個“AND”條件,所以被稱為AND 同步,也被稱為同時wait操作,即Swait(Simultaneous wait),相應的signal被稱為Ssignal(Simultaneous signal) 技術分享圖片 Swait(S) 和 Ssignal(S)可以描述為:
Swait(S1,S2,…,Sn)
        if(Si>=1 and …  and Sn>=1){
          for( i=1 to n){
          Si=Si-1;
          }
        }else{
        將進程插入到第一個資源<1 的對應的S的隊列中,並且程序計數器設置到Swait的開始(比如S1 S2 都大於等於1,但是 S3<1,那麽就插入到S3.L中;從頭再來就是為了整體分配)
        }

  Ssignal(S1,S2,…,Sn)
          for(i=1 to n){
          Si=Si+1;
          將與Si關聯的所有進程從等待隊列中刪除,移入到就緒隊列中
          }
AND型信號量機制借助於對於多個資源的“批量處理”的原子操作方式,將多資源的同步問題,轉換為一個“同一批資源”的同步問題

信號量集

記錄型信號量機制中,只是對信號量加1(S+1 或者S.value+1) 或者 減1(S-1 或者S.value-1)操作,也就是只能獲得或者釋放一個單位的臨界資源 如果想要一次申請N個呢? 如果進行N次的資源申請怎麽辦,一種方式是可以使用多次Wait(S)操作,但是顯然效率較低; 另外有時候當資源數量低於某一下限值時,就不進行分配怎麽辦?需要在每次分配資源前檢查資源的數量。 為了解決這兩個問題,對AND信號量機制進一步擴展,形成了一般化的“信號量集”機制。 Swait操作可描述如下 其中S為信號量,d為需求值,而 t為下限值。
Swait(S1,t1,d1,…,Sn,tn,dn)
  if( Si>=t1 and …  and Sn>=tn){
          for(i=1 to n){
                  Si=Si-di;
          }
  }else{
          將進程插入到第一個資源Si<ti 的對應的S的隊列中,並且程序計數器設置到Swait的開始(比如S1 S2 都大於等於t1,t2,但是 S3<t3,那麽就插入到S3.L中;從頭再來就是為了整體分配)    
  }

Ssignal(S1,d1,…,Sn,dn)
         for(i=1 to n){
                  Si=Si+di;
                將與Si關聯的所有進程從等待隊列中刪除,移入到就緒隊列中
          }
信號量集就是AND型信號量機制將資源限制擴展到Ti
  • Swait(S,d,d)。此時在信號量集中只有一個信號量 S,但允許它每次申請 d 個資源,當現有資源數少於d時,不予分配。
  • Swait(S,1,1)。此時的信號量集已蛻化為一般的記錄型信號量(S>1時)或互斥信號量(S=1 時)。
  • Swait(S,1,0)。這是一種很特殊且很有用的信號量操作。當 S≥1 時,允許多個進程進入某特定區;當 S 變為 0 後,將阻止任何進程進入特定區。換言之,它相當於一個可控開關。
上面的格式為(s,t,d)也就是第一個為信號量,第二個為限制,第三個為需求量 所以Swait(S,1,0)可以用做開關,只要S>=1,>=1時可分配,每次分配0個,所以只要S>=1,永遠都進的來,一旦S<1,也就是0往後,那麽就不滿足條件,就一個都進不去

小結

臨界區機制通過算法控制進程串行進入臨界區,而信號量機制則是借助於原語操作(原子性)對臨界資源進行訪問控制 按照各種信號量機制對應的規則以及相應的原語操作,就可以做到對資源的共享同步訪問,而不會出現問題 信號量機制總共有四種 技術分享圖片 整型信號量機制可以處理同一共享資源中,資源數目不止一個的情況 記錄型信號量對整型信號量機制的“忙等”進行了優化,通過block以及weakup原語進行阻塞和通知 AND型信號量機制解決了對於多個共享資源的同步 信號量集是對AND的再一次優化,既能夠處理多個共享資源同步的問題,還能夠設置資源申請的下限,是一種更加通用的處理方式

信號量的應用

實現資源互斥訪問

為使多個進程能互斥地訪問某臨界資源,只須為該資源設置一互斥信號量 mutex,並設其初始值為1 然後將各進程訪問該資源的臨界區 CS置於 wait(mutex)和 signal(mutex)操作之間即可。 這樣,每個欲訪問該臨界資源的進程在進入臨界區之前,都要先對 mutex 執行wait操作,若該資源此刻未被訪問,本次wait操作必然成功,進程便可進入自己的臨界區,否則進程將會阻塞。 步驟: ....... wait(mutex); 臨界區 signal(mutex); 剩余區 .......

實現前趨關系

前驅關系就是指執行順序,比如要求語句S1執行結束之後才能執行語句S2 在進程P1中: S1; signal(S); 在進程P2中 wait(S); S2; 顯然,初始時將資源S設置為0,S2需要獲取到資源才會執行,而S1執行後就會釋放資源 對於一個更加復雜的前驅關系圖,如何實現? 技術分享圖片 從圖中可以看得出來,有S2和S3依賴S1 S4 和 S5依賴S2,而S6依賴S3、S4、S5 所以,S1應該提供兩個信號量,提供給S2和S3 使用 S2 應該等待S1的信號量,並且提供兩個信號量給S4 和 S5 S3 應該等待S1的信號量,並且提供一個信號量給S6 S4應該等待S2的信號量,並且提供一個給S6 S5應該等待S2的信號量,並且提供一個給S6 S6應該等待S3、S4、S5 所以總共需要2+2+1+1+1 6個信號量,我們取名為a,b,c,d,e,f,g 技術分享圖片 那麽過程如下:
Var a,b,c,d,e,f,g:semaphore: =0,0,0,0,0,0,0;
P1{
        S1;
        signal(a);
        signal(b);
}
P2{
        wait(a);
        S2;
        signal(c);
        signal(d);
}
P3{
        wait(b);
        S3;
        signal(e);
}
P4{
        wait(c);
        S4;
        signal(f);
}
P5{
        wait(d);
        S5; 
        signal(g);
}
P6{
        wait(e);
        wait(f); 
        wait(g);
        S6;
}
有人可能會疑惑,這裏的wait和signal又是什麽?他就是信號量機制中的 wait 和 signal,他的內容是相當於下面的這些 技術分享圖片

同步方式之管程

雖然信號量機制是一種既方便、又有效的進程同步機制但每個要訪問臨界資源的進程都必須自備同步操作 wait(S)和 signal(S),這就使大量的同步操作分散在各個進程中。 這不僅給系統的管理帶來了麻煩,而且還會因同步操作的使用不當而導致系統死鎖在解決上述問題的過程中,便產生了一種新的進程同步工具——管程(Monitors)。 所以管程也可以這麽理解:它能夠確保臨界資源的同步訪問,並且還不用將大量的同步操作分散到各個進程中。

管程的定義

系統中的各種硬件資源和軟件資源,均可用數據結構抽象地描述其資源特性 比如一個IO設備,有狀態(空閑還是忙時?),以及可以對他采取的操作(讀取還是寫入?)以及等待該資源的進程隊列來描述,所以就可以從這三個維度抽象描述一個IO設備,而不關註他們的內部細節 技術分享圖片 又比如,一個集合,可以使用集合大小,類型,以及一組可執行操作來描述,比如Java中的ArrayList,有屬性,還有方法 技術分享圖片 可以把對該共享數據結構實施的操作定義為一組過程 比如資源的請求和釋放過程定義為request 和 release 進程對共享資源的申請、釋放和其它操作,都是通過這組過程對共享數據結構的操作來實現的 類比到JavaBean的話,這些操作就如同setter和getter方法,所有對於指定對象的操作訪問都需要通過getter和setter方法 ,類似,所有對共享數據結構實施的操作,都需要借助於這一組過程。 技術分享圖片 這組過程還可以根據資源的情況,或接受或阻塞進程的訪問,確保每次僅有一個進程使用共享資源,這樣就可以統一管理對共享資源的所有訪問,實現進程互斥 還是類比到JavaBean,就是相當於又增加了幾個方法,這些方法提供了更多的邏輯判斷控制 代表共享資源的數據結構,以及由對該共享數據結構實施操作的一組過程所組成的資源管理程序,共同構成了一個操作系統的資源管理模塊,我們稱之為管程。 Dijkstra於1971年提出: 把所有進程對某一種臨界資源的同步操作都集中起來,構成一個所謂的秘書進程。 凡要訪問該臨界資源的進程,都需先報告秘書,由秘書來實現諸進程對同一臨界資源的互斥使用。 管程的概念經由Dijkstra提出的概念演化而來,由Hoare和Hanson於1973年提出。 定義如下 一組相關的數據結構和過程一並稱為管程。 Hansan的定義:一個管程定義了一個數據結構和能為並發進程在該數據結構上所執行的一組操作,這組操作能同步進程和改變管程中的數據。 所以管程的核心部分是對共享數據抽象描述的數據結構以及可以對該數據結構實施操作的一組過程使用數據結構對共享資源進行抽象描述,那麽必然要初始化數據,比如一個隊列Queue,有屬性size,這是一個抽象的數據結構,那麽一個具體的隊列到底有多大?你需要設置size 的值,所以對於管程還包括初始化 技術分享圖片 一個基本的管程定義如下:
Minitor{
  管程內部的變量結構以及說明;
  函數1(){

  }
  ......
  函數N(){

  }
  init(){
       對管程中的局部變量進行初始化;
  }
}

管程特點

管程就是管理進程,管程的概念就是設計模式中“依賴倒置原則”,依賴倒置原則是軟件設計的一個理念,IOC的概念就是依賴倒置原則的一個具體的設計 管程將對共享資源的同步處理封閉在管程內,需要申請和釋放資源的進程調用管程,這些進程不再需要自主維護同步。有了管程這個大管家(門面模式?)進程的同步任務將會變得更加簡單。 管程是墻,過程是門,想要訪問共享資源,必須通過管程的控制(通過城墻上的門,也就是經過管程的過程) 而管程每次只準許一個進程進入管程,從而實現了進程互斥 技術分享圖片 管程的核心理念就是相當於構造了一個管理進程同步的“IOC”容器。 管程是一個語言的組成成分(非操作系統支持部分),管程的互斥訪問完全由編譯程序在編譯時自動添加上,無需程序員關心,而且保證正確 一般的 monitor 實現模式是編程語言在語法上提供語法糖,而如何實現 monitor 機制,則屬於編譯器的工作 比如,Java中使用synchronized時,這是不是一種管程理念?你只是寫了一個synchronized關鍵字(語法糖),多線程的共享同步完全不用你操心了 (註意:並不是所有的語言都支持管程的概念技術分享圖片

條件變量

管程可以保證互斥,同一時刻僅有一個進程進入管程,所以他必然需要同步工具,如兩個同步操作原語 wait和 signal,他還需要互斥量用以控制管程進入的同步 當某進程通過管程請求獲得臨界資源而未能滿足時,管程便調用 wait 原語使該進程等待,並將其排在等待隊列上 僅當另一進程訪問完成並釋放該資源之後,管程調用signal原語,喚醒等待隊列中的隊首進程 但是,僅僅這個互斥量是不夠的 比如,如果需要處理之前提到過的“執行順序控制”,如何控制前驅關系? 當一個進程調用了管程,在管程中時被阻塞或掛起,直到阻塞或掛起的原因解除,而在此期間,如果該進程不釋放管程,則其它進程無法進入管程,被迫長時間地等待。 所以還需要其他的信號量用於針對其他條件進行同步,這些就是條件變量,所以一個完整的管程定義為:
Minitor{
  管程內部的變量結構以及說明;
  condition  條件變量列表;
  函數1(){

  }
  ......
  函數N(){

  }
  init(){
          對管程中的局部變量進行初始化;
  }
}
條件變量就是當調用管程過程的進程無法運行時,用於阻塞進程的一種信號量 管程中對每個條件變量都須予以說明,其形式為:Var x,y:condition。 對條件變量的操作僅僅是wait和signal,條件變量也是一種抽象數據類型,每個條件變量保存了一個鏈表,用於記錄因該條件變量而阻塞的所有進程,同時提供的兩個操作即可表示為 x.wait和x.signal,其含義為: ① x.wait:正在調用管程的進程因 x 條件需要被阻塞或掛起,則調用 x.wait 將自己插入到x條件的等待隊列上,並釋放管程,直到x條件變化。此時其它進程可以使用該管程。 ② x.signal:正在調用管程的進程發現 x 條件發生了變化,則調用 x.signal,重新啟動一個因 x 條件而阻塞或掛起的進程。如果存在多個這樣的進程,則選擇其中的一個,如果沒有,則繼續執行原進程,而不產生任何結果。這與信號量機制中的 signal操作不同,因為後者總是要執行s:=s+1操作,因而總會改變信號量的狀態。 如果有進程Q因x條件處於阻塞狀態, 當正在調用管程的進程P執行了x.signal操作後,進程Q 被重新啟動,此時兩個進程 P和Q,如何確定哪個執行,哪個等待,可采用下述兩種方式之一進行處理: (1) P等待,直至Q 離開管程或等待另一條件。 (2) Q等待,直至P離開管程或等待另一條件。 采用哪種處理方式,當然是各執一詞。 Hoare 采用了第一種處理方式 而 Hansan 選擇了兩者的折衷,他規定管程中的過程所執行的signal 操作是過程體的最後一個操作,於是,進程P執行signal操作後立即退出管程,因而進程Q馬上被恢復執行。

總結

進程控制是操作系統的一種硬性管理,是必須要有的,如果沒有進程控制,就沒辦法合理安排運行進程, 根本無法完成狀態的切換維護等。 進程的同步是一種軟邏輯上的,如果不做,並不會導致系統出問題或者進程無法運行,但是如果不進行同步,結果卻很可能是錯誤的,所以也是必須做的 類比裝修的話,進程控制就是硬裝,不裝沒法住,總歸要水電搞搞好,進程同步就是家具家電和軟裝,硬裝後住進去不會出現“生存問題”(至少有水喝有電用),但是你要是連個熱水壺都沒有是打算要喝涼水麽 進程同步的概念多很復雜抽象,因為畢竟是概念表述,沒有涉及到具體的實現細節。 進程同步的核心是對於臨界資源的訪問控制,也就是對於臨界區的訪問。 不管是臨界區算法還是信號量機制還是管程機制,終歸也都是控制進入臨界區的不同的實現思路。 每種不同的算法、機制都各自有各自的特色,場景。 信號量機制將臨界資源的訪問的互斥,演化為可以多個進程訪問資源(整型信號量),記錄型信號量對整型信號量機制進行優化,處理了忙等的問題 然後繼續演化出AND型,可以對不同的資源進行同步,而不僅僅是同一種資源 最後發展為信號量集的形式,可以對不同的資源、不同的個數、不同的限制進行處理,變得更為通用。 管程更多的是一種設計思維,管程就是管理進程的程序,進程對於資源的同步操作全都依賴管程這一“大管家”,管程是編程語言級別的,不需要程序員進行過多的處理,一般會提供語法糖 需要註意並不是所有的語言都有管程的概念(Java是有的),管程讓你從同步的細節中解放出來,可以在很多場景下簡化同步的實現。 管程的概念是“線程同步”的“IOC”,大大簡化了同步的代價。 不管臨界區算法還是信號量機制還是借助於管程,他們都是一種同步工具,可以認為他們就是一組“方法”,“方法”的邏輯就是本文前面介紹的原理 在需要進程同步問題的解決思路中,可以直接使用“封裝好的方法” 以上,盡管都是在說操作系統關於進程的同步的處理,其實,在同步問題上進程和線程的設計理念是相通的 因為這本質上都是在說並發----多道程序運行的操作系統,通常使用輪轉時間片的方式並發的運行多個進程 原文地址:進程同步概念簡介 多線程上篇(四)

進程同步概念簡介 多線程上篇(四)