經典進程同步問題一:生產者-消費者問題(The producer-consumer problem)


(注:參考教材:計算機操作系統第四版 西安電子科技大學出版社)

      問題描述:一群生產者進程在生產產品,並將這些產品提供給消費者去消費。為了使生產者進程與消費者進程能夠並發進行,在兩者之間設置一個具有n個緩沖區的緩沖池,生產者進程將產品放入一個緩沖區中;消費者可以從一個緩沖區取走產品去消費。盡管所有的生產者進程和消費者進程是以異方式運行,但它們必須保持同步:當一個緩沖區為空時不允許消費者去取走產品,當一個緩沖區滿時也不允許生產者去存入產品。

      解決方案:我們這里利用一個一個數組buffer來表示這個n個緩沖區的緩沖池,用輸入指針和輸出指針+1來表示在緩沖池中存入或取出一個產品。由於這里的緩沖池是循環緩沖的,故應把in和out表示成:in = ( in +1 ) % n (或把out表示為 out = ( out +1 ) % n )當( in +1) % n= out的時候說明緩沖池滿,in = out 則說明緩沖池空。在這里還要引入一個整型的變量counter(初始值0),每當在緩沖區存入或取走一個產品時,counter +1或-1。那么問題的關鍵就是,把這個counter作為臨界資源處理,即令生產者進程和消費者進程互斥的訪問它。                 

     首先解釋一下信號量(Semaphores)機制。這是荷蘭學者Dijkstra在1965年提出的一種有效的進程同步工具。Dijkstra定義的整型信號量是一個用於表示資源數目的整型量,不同於一般的整型量,它除了初始化以外只能通過兩個標准的原子操作(Atomic Operation)waits(S)和signal(S)來訪問。很長時間以來這兩個操作分別被稱為P、V操作。wait和signal操作描述如下:

 1 wait(S){
 2 
 3         while (S <= 0); //無可用資源
 4         S-- ;
 5 }
 6 
 7 signal(S){
 8 
 9         S-- ;
10 }

waits(S)和signal(S)是原子操作,執行時不可中斷。即:當一個進程在修改某個信號量時,沒有其他進程可以對其修改。

 

1.利用記錄型信號量解決問題

      上述整型信號量機制中,只要S<=0就會不斷的測試,沒有遵循“讓權等待”准則,可能導致“忙等”。記錄型信號量機制采取“讓權等待”,但會出現多個進程等待訪問同一個臨界資源的情況,因此在此機制中,除了一個表示資源數量的整型量value以外,還需要增加一個進程鏈表指針list,用於鏈接所有等待進程。

      記錄型信號量的數據項描述如下:

1 typedef struct{
2 
3         int value ;
4         struct processControlBlock *list ;
5 
6 }semaphore;

相應的wait(S)和signa(S)為:

 1 wait(semaphore *S){
 2 
 3         S->value -- ;
 4         if( S->value < 0 )
 5                 block( S->list ); //資源分配完畢,自我阻塞
 6         
 7 }
 8 
 9 signal(semaphore *S){
10 
11         S->value ++ ;
12         if( S->value <= 0 )
13                 weakup( S->list ); //仍有等待該資源的進程被阻塞,將list中的第一個進程喚醒
14         
15 }

      現在是對生產者-消費者問題解決方案的描述:

     假定在生產者和消費者之間的公共緩沖池中具有n個緩沖區,利用互斥信號量mutex來實現諸進程對緩沖池的互斥使用;利用信號量empty和full分別表示緩沖池中的空槽數量和滿槽數量。

 1 int in = 0 , out = 0 ;
 2 item buffer[n];
 3 semaphore mutex = 1 ; //信號量,控制對臨界資源的互斥訪問
 4 semaphore empty = n ; //信號量,表示緩沖池當前的空槽數目
 5 semaphore full = 0 ; //信號量,表示緩沖池當前的滿槽數目
 6 
 7 //producer 
 8 void producer {
 9     
10     do {
11 
12         item = produce_item();
13         wait(empty); //空槽數目減一
14         wait(mutex); //進入臨界區
15         buffer[in] = insert_item(item); //存入產品
16         in = ( in +1 ) % n ;
17         signal(mutex); //退出臨界區
18         signal(full); //滿槽數目加一
19 
20            } while(121 }
22 
23 //consumer 
24 void consumer{
25     
26     do {
27 
28         wait(full); //滿槽數目減一
29         wait(mutex); //進入臨界區
30         remove_item(item) = buffer[out] ; //取出產品
31         out = ( in +1 ) % n ;
32         signal(mutex); //退出臨界區
33         signal(empty); //空槽數目加一
34         consumer_item(item);
35            } while(136 }
37 
38 void main() {
39 
40     cobegin //並發執行
41     producer();consumer();
42     coend
43 
44 }

注意:

1.用於實現互斥的wait(mutex) 和signal(mutex)必須成對出現,而對資源的full和empty的wait和signal也要成對出現,這里是指分別出現在不同的程序中。

2.應該先進行對資源的wait操作再進行對互斥信號量的wait操作。

 

2.利用AND信號量解決問題

        首先還是解釋一下AND型信號量。上述是針對多個並發程序僅共享一個臨界資源的情況,AND型信號量則是解決多個並發程序共享多個資源的問題。基本思想是:對若干個臨界資源的分配采取原子操作的方法,要么把它請求的所有資源都分配到進程,要么就一個也不分配。這里用Swait()表示要么全部分配,要么全不分配,用Ssignal()表示全部釋放。

Swait() 和Ssignal的描述如下:

 1 Swait(S1,S2,S3,...,Sn){
 2     while (1) {
 3         if( S1>=1 && S2>=1 && ... && Sn>=1){
 4 
 5         for( i = 1 ; i <= n ; i++) Si --;
 6         break ;
 7             }else{
 8 
 9             將這個進程放進等待隊列,第一個<1的Si作為等待隊列的開始
10             }
11     }    
12 }
13 
14 Ssignal(S1,S2,...,Sn) {
15 
16     while(1) {
17         for( i = 1 ; i <= n ; i++) {
18         Si ++;
19         清空等待隊列並將其存入就緒隊列
20         }
21     }
22 }

具體實現:

 1 consumer_item(item);
 2 
 3 int in = 0 , out = 0 ;
 4 item buffer[n];
 5 semaphore mutex = 1 ; //信號量,控制對臨界資源的互斥訪問
 6 semaphore empty = n ; //信號量,表示緩沖池當前的空槽數目
 7 semaphore full = 0 ; //信號量,表示緩沖池當前的滿槽數目
 8 
 9 //producer 
10 void producer {
11     
12     do {
13 
14         item = produce_item();
15         Swait(empty,mutex); 
16         buffer[in] = insert_item(item); //存入產品
17         in = ( in +1 ) % n ;
18         signal(mutex,full); 
19 
20 
21            } while(122 }
23 
24 //consumer 
25 void consumer {
26     
27     do {
28 
29         wait(full,mutex); 
30         remove_item(item) = buffer[out] ; //取出產品
31         out = ( in +1 ) % n ;
32         signal(mutex,empty); 
33             consumer_item(item);
34            } while(135 }
36 
37 void main() {
38 
39     cobegin //並發執行
40     producer();consumer();
41     coend
42 
43 }

3.利用管程解決問題

    首先介紹一下管程。代表共享資源的數據結構以及對該數據結構實施操作的一組過程所組成的資源管理程序共同構成了一個操作系統的資源管理模塊,稱為管程。

    這里先定義一個管程PC,描述如下:

 1 Monitor PC {
 2 
 3     item buffer[N];
 4     int in , out ;
 5     condition notfull , notempty; //條件變量
 6     int count ; //緩沖池中已有的產品數目
 7 
 8     public:
 9     //沒滿就往里存,滿了就阻塞
10     void put( item x ){ 
11 
12     if( count >= N)
13         cwait( notfull ); //緩沖池滿,阻塞,掛在條件condition的隊列上
14         buffer[in] = x;
15             in = ( in +1 ) % n ;
16         count ++ ;
17         csignal(notempty); // 喚醒一個阻塞在condition隊列中的進程,隊列空則無操作
18     }
19 
20     void get( item x ){
21 
22     if( count <= 0)
23         cwait( notempty ); //緩沖池空,阻塞,掛在條件condition的隊列上
24         x = buffer[out];
25             out = ( out +1 ) % n ;
26         count -- ;
27         csignal(notfull); // 喚醒一個阻塞在condition隊列中的進程,隊列空則無操作
28     }
29 
30     { in = 0, out = 0 , count = 0 ;}
31 
32 }PC;

具體方案:

 1 //producer
 2 void producer(){
 3 
 4     item x;
 5     while(1){
 6 
 7     x = produce_item();
 8     PC.put(x);
 9     }
10 }
11 
12 //consumer
13 
14 void consumer(){
15 
16     item x;
17     while(1){
18 
19     PC.get(x);
20     consumer_item() = x ;
21     
22     }
23 }
24 
25 void main() {
26 
27     cobegin //並發執行
28     producer();consumer();
29     coend
30 
31 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM