本文為博主原創文章,未經博主允許不得轉載 http://www.cnblogs.com/kiplove/p/6745335.html
涉及進程同步的一些概念:
互斥與同步:
臨界資源(臨界區):指一次只能允許一個進程使用的共享資源稱為臨界資源;
同步:指為完成某種任務而建立的兩個和多個進程,這些進程在合作的過程中需要協調工作次序進行有序的訪問而出現等待所產生的制約關系。
互斥:指兩個或多個進程訪問臨界資源時只能一個進程訪問,其他進程等待的一種相互制約的關系。
信號量與互斥量:
信號量:本身是一個計數器,使用P,V兩個操作來實現計數的減與加,當計數不大於0時,則進程進入睡眠狀態,它用於為多個進程提供共享數據對象的訪問。
互斥量:如果信號量只存在兩個狀態,那就不需要計數了,可以簡化為加鎖與解鎖兩個功能,這就是互斥量。
一、生產者與消費者問題
問題描述:一組生產者進程和一組消費者進程共享一塊初始為空,大小確定的緩沖區,只有當緩沖區為滿時,生產者進程才可以把信息放入緩沖區,否則就要等待;只有緩存區不為空時,消費者進程才能從中取出消息,否則就要等待。緩沖區一次只能一個進程訪問(臨界資源)。
問題分析:生產者與消費者進程對緩沖區的訪問是互斥關系,而生產者與消費者本身又存在同步關系,即必須生成之后才能消費。因而對於緩沖區的訪問設置一個互斥量,再設置兩個信號量一個記錄空閑緩沖區單元,一個記錄滿緩沖區單元來實現生產者與消費者的同步。
問題解決:偽代碼實現
semaphore mutex=1; semaphore full=0; //滿緩沖區單元 semaphore empty=N; //空閑緩沖區單元 prodecer() { while(1) { P(empty); P(mutex); add_source++; V(mutex); V(full); } } consumer() { while(1) { P(full); P(mutex); add_source--; V(mutex); V(empty); } }
二、讀者與寫者問題
問題描述:有讀者與寫者兩個並發進程共享一個數據,兩個或以上的讀進程可以訪問數據,但是一個寫者進程訪問數據與其他進程都互斥。
問題分析:讀者與寫者是互斥關系,寫者與寫者是互斥關系,讀者與讀者是同步關系。因而需要一個互斥量實現讀與寫和寫與寫互斥,一個讀者的訪問計數和實現對計數的互斥。
問題解決:三種偽代碼實現
1、讀者優先
讀者優先,只要有讀者源源不斷,寫者就得不到資源。容易造成寫者飢餓。
1 //讀者優先 2 3 int count=0; 4 semaphore mutex=1; //讀者計數鎖 5 semaphore rw=1; //資源訪問鎖 6 7 writer() 8 { 9 while(1) 10 { 11 P(rw); 12 writing sth; 13 V(rw); 14 } 15 } 16 17 reader() 18 { 19 while(1) 20 { 21 P(mutex); 22 if(count==0) 23 P(rw); 24 count++; 25 V(mutex); 26 reading sth; 27 P(mutex); 28 count--; 29 if(count==0) 30 V(rw); 31 V(mutex); 32 } 33 }
2、讀寫公平
讀者與寫者公平搶占資源,但是只要之前已經排隊的讀者,就算寫者獲取的資源,也要等待所有等待的讀者進程結束。
1 //讀寫公平 2 int count=0; 3 semaphore mutex=1; //讀者計數鎖 4 semaphore rw=1; //資源訪問鎖 5 semaphore w=1; //讀寫公平搶占鎖 6 writer() 7 { 8 while(1) 9 { 10 P(w); 11 P(rw); 12 writing sth; 13 V(rw); 14 V(w); 15 } 16 } 17 18 reader() 19 { 20 while(1) 21 { 22 P(w); 23 P(mutex); 24 if(count==0) 25 P(rw); 26 count++; 27 V(mutex); 28 V(w); 29 reading sth; 30 P(mutex); 31 count--; 32 if(count==0) 33 V(rw); 34 V(mutex); 35 } 36 }
3、寫者優先
寫者優先,只要寫者源源不斷,讀者就得不到資源,但是在這之前已經排隊的的讀者進程依然可以優先獲得資源,在這之后則等待所有寫者進程的結束。這種也易造成讀者飢餓。
1 //寫者優先 2 int write_count=0; //寫計數 3 int count=0; //讀計數 4 semaphore w_mutex=1; //讀計數時鎖 5 semaphore r_mutex=1; //寫計數時鎖 6 semaphore rw=1; //寫優先鎖 7 semaphore source=1; //資源訪問鎖 8 9 writer() 10 { 11 while(1) 12 { 13 P(w_mutux); 14 if(write_count==0) 15 P(rw); //獲得則只要有寫進程進來就不釋放 16 write_count++; 17 V(w_mutux) 18 19 P(resouce); //寫時互斥必須加資源獨占的鎖 20 writing sth; 21 V(resouce); 22 23 P(w_mutux); 24 write_count--; 25 if(write_count==0) 26 V(rw); 27 V(w_mutux); 28 } 29 } 30 31 reader() 32 { 33 while(1) 34 { 35 P(rw); //使用了立即釋放 36 P(r_mutex); 37 if(count==0) 38 P(resouce); 39 count++; 40 V(r_mutex); 41 V(rw); 42 43 reading sth; 44 45 P(r_mutex); 46 count--; 47 if(count==0) 48 V(resouce); 49 V(r_mutex); 50 } 51 }
三、哲學家就餐問題
問題描述:一張圓桌上坐着五名哲學家,每兩名哲學家之間的桌子擺一根筷子,哲學家只有同時拿起左右兩根筷子時才可以用餐,用餐完了筷子放回原處。
問題分析:這里五名哲學家就是五個進程,五根筷子是需要獲取的資源。可以定義互斥數組用於表示五根筷子的互斥訪問,為了防止哲學家個取一根筷子出現死鎖,需要添加一定的限制條件。一種方法是限制僅當哲學家左右筷子均可以用時,才拿起筷子,這里需要一個互斥量來限制獲取筷子不會出現競爭。
問題解決:一次僅能一個哲學家拿起筷子,效率比較低。
1 semaphore chopstick[5]={1,1,1,1,1}; 2 semaphore mutex=1; 3 pi() 4 { 5 while(1) 6 { 7 P(mutex); 8 P(chopstick[i]); 9 P(chopstick[(i+1)%5]); 10 V(mutex); 11 12 eating; 13 14 V(chopstick[i]); 15 V(chopstick[(i+1)%5]); 16 } 17 }
補充內容
死鎖:如果一個進程集合中的每個進程都在等待只能由該進程集合中的其他進程才能引發的事件,那么該進程集合就是死鎖的。
死鎖的條件(四個同時滿足):
(1)互斥:每個資源要么已經分配給一個進程,要么就是可用的;
(2)占有和等待:已經得到的某個資源的進程請求新的資源;
(3)不可搶占:已經分配的資源不能強制被搶占,只能進程自己顯示的釋放;
(4)環路等待:存在一種進程資源的循環等待鏈。
死鎖的處理策略:
(1)死鎖預防:破壞死鎖的四個條件之一
破環互斥條件:允許資源共享
破環占有和等待條件:采用預先靜態分配
不可搶占:請求新資源得不到時,釋放已經保持占有的資源,待以后重新申請
環路等待:采用順序資源分配法
(2)死鎖避免:死鎖避免事先預防策略,但是是采用資源動態分配的過程中,防止系統進入不安全狀態,以避免死鎖。
銀行家算法:可利用資源矢量Available,請求矢量Request
最大需求矩陣Max,分配矩陣Allocation,需求矩陣Need
通過Need=Max-Allocation獲得每個進程需要的各類資源數Need矩陣
一般每個進程請求矢量應該小於等於Need的值
試探分配:Available=Avaliable-Request
Allocate相對應的項=Allocate相對應的項+Request
Need相對應的項=Need相對應的項-Request
安全性算法:檢查資源分配后,系統是否屬於安全狀態,如果安全才正式分配資源,否則作廢。一般通過安全性算法推算一個安全序列(核心)。
(3)死鎖檢測與解除:
檢測死鎖:利用死鎖原理化簡資源分配圖檢測死鎖的存在
死鎖解除:資源剝奪、撤銷進程、進程回退