信號量 進程 (m個生產者,n個消費者,容量爲r的緩衝區)

轉載,原文地址http://www.cnblogs.com/phinecos/archive/2006/08/25/486552.html  

 1.整型信號量是一個整數變量,除初始化外,對其只能執行兩個操作,即wait(s)和signal(s),也叫p(s)和v(s)操作,均是原語操作,用來實現進程的同步,互斥.
 2.記錄型信號量

type semaphore = record
            value:integer
            L: list of process;
    end
    procedure wait(s)
        var s: semaphore;
            begin
                s.value = s.value - 1;
                if(s.value<0) then block(s.L)
            end
    procedure signal(s)
        var s: semaphore
            begin
                s.value = s.value +1;
                if(s.value<=0) then wakeup(s.L)
            end


   結論:
    (1) 若信號量s爲正值,則等於在封鎖進程之前對信號樑可以施行的p操作數,也就是s所代表的實際可以使用的物理資源數.
    (2) 若信號量s爲負值,則其絕對值等於排列在該信號量s隊列之中等待的進程個數,也就等於對信號量s實施p操作而被封鎖起來並進入信號量s隊列的進程數.
    (3)p操作一般代表請求一個資源,v操作一般代表着釋放一個資源,在一定條件下,p操作代表掛起進程操作,v操作代表喚醒被掛起的進程.
    3.經典同步問題
    1)生產者/消費者問題
           a) 問題一:有一個生產者和一個消費者,共享一個緩衝區.兩者要互斥訪問緩衝區.
        解:定義兩個信號量:empty表示緩衝是否爲空,初值爲1,即初始時可以存入一件物品.full表示緩衝區中是否有物品,初值爲0,即初始時緩衝區沒有物品.

            程序:
                    begin 
                        buffer:integer
                        empty,full:semaphore=1,0;
                        cobegin
                        process Producer
                            begin
                                L1:生產一件物品
                                wait(empty);
                                 buffer = product;
                                 signal(full);
                                 goto L1;
                            end
                        process Consumer
                            begin
                                L2:wait(full);
                                從緩衝區取出一件物品;
                                signal(empty);
                                消費掉物品
                                goto L2;
                           end
                        coend
                    end


注:
    (1)進程互斥只需要一個信號量,而同步可能要兩個信號量.
    (2)p,v操作仍然要成對,但在進程進入臨界區前後調用的是針對不同信號量的wait,signal操作,而進程互斥時是針對相同的信號量.
    (3)至少有一個信號量的初值>=1,否則所有進程無法執行,一般是指管理是否允許訪問共享資源的那個信號量.如這裏的empty設爲1,如果緩衝區容量爲n,則可以設爲n;
    
    b)問題二:m個生產者,n個消費者,容量爲r的緩衝區,(m,n,r都大於1),不要生產者和消費者互斥存取物品.
        解:1)生產者和消費者之間要同步,類似問題一,用兩個信號量empty,full;2)m個生產者之間要互斥,n個消費者之間也要互斥,但生產者和消費者不用互斥存取物品,因此設兩個計數器in,out和相應的互斥信號量mutex1,mutex2

  程序:
            begin
                buffer:array[0r-1]: integer
                in,out:integer=0,0;
                empty=r,full=0,mutex1=1,mutex2=1:semaphore;
                cobegin
                    process Producer-i(i=1,2m)
                    begin
                        L1:生產一件物品;
                        wait(empty)
                        wait(mutex1)
                         buffer[in] = product;
                         in = (in+1)mod r;
                        signal(mutex1)
                        signal(full)
                        goto L1;
                    end
                    process Consumer-j(j=1,2n)
                    begin
                        L2: wait(full)
                        wait(mutex2)
                        take  a product from buffer[out];
                        out =(out+1)mod r;
                        signal(mutex2)
                        signal(empty)
                        消費一件物品;
                        goto L2;
                    end


    c)問題三:m個生產者,n個消費者,容量爲r的緩衝區,(m,n,r都大於1),要求生產者和消費者互斥存取物品.
       解:1)生產者和消費者之間要同步,類似問題一,用兩個信號量empty,full;2)m個生產者之間要互斥,n個消費者之間也要互斥,同時要求生產者和消費者互斥存取物品,因此設兩個計數器in,out和一個互斥信號量mutex.

代碼:

begin
                buffer:array[0r-1]: integer
                in,out:integer=0,0;
                empty=r,full=0,mutex=1:semaphore;
                cobegin
                    process Producer-i(i=1,2m)
                    begin
                        L1:生產一件物品;
                        wait(empty)
                        wait(mutex)
                         buffer[in] = product;
                         in = (in+1)mod r;
                        signal(mutex)
                        signal(full)
                        goto L1;
                    end
                    process Consumer-j(j=1,2n)
                    begin
                        L2: wait(full)
                        wait(mutex)
                        take  a product from buffer[out];
                        out =(out+1)mod r;
                        signal(mutex)
                        signal(empty)
                        消費一件物品;
                        goto L2;
                    end

注:應該先執行對資源信號量的p,v操作,再執行互斥信號量的p,v操作,否則會引起死鎖.

2)讀者/寫者問題
    要求:(1)允許多個讀者同時從數據區讀數據;
            (2)當讀者在讀數據時,不允許寫者寫數據
            (3)任何時候只允許一個寫者向數據區寫數據;
            (4)若有寫者在寫數據,則不允許讀者讀數據.
問題分爲讀者優先和寫者優先兩類。若有讀者在讀數據時,寫者到來時就會被阻塞,直到所有讀者讀完數據才被喚醒.這裏讀者優先於寫者,稱爲第一類讀寫問題.若讀者在讀數據時,有寫者到來,則後續的讀者將被阻塞,直到寫者離開才被喚醒,這類問題中寫者處於優勢地位,叫第二類讀寫問題.

    a)問題一:用記錄型信號量解第一類讀寫問題

begin
               var Rmutex,WRmutex:semaphore = 1,1;
                readcount:integer=0;
                cobegin

                    process Reader
                    begin
                       repeat
                        wait(Rmutex)
                       readcount = readcount+1;
                      if(readcount==1) wait(WRmutex);//第一個讀者開始讀數據,禁止寫者訪問
                       signal(Rmutex)
             讀數據
                        wait(Rmutex)
                       readcount = readcount-1;
                      if(readcount==0) signal(WRmutex);//沒有讀者讀數據喚醒寫者訪問
                       signal(Rmutex)
        until false
                    end

                    process Writer
                    begin
                        repeat
                        wait(WRmutex)
                         寫數據
                        signal(WRmutex)
         until false
                    end
    coend
  end 

注:
    (1)WRmutex用於讀者和寫者,寫者和寫者之間互斥訪問數據區.
    (2)當沒有讀者訪問時,允許一個讀者進入,第一個讀者進入時要用WRmutex與寫者互斥.
    (3) readcount記錄正在讀數據區的讀者數量,由於能被多個讀者進程共享,也是臨界資源,因此用Rmutex來對其實施互斥訪問.
    (4)允許多個讀者同時讀數據,當至少有一個讀者在讀時,其他讀者不用等待就可以讀數據。
    (5)一旦有一個讀者開始讀數據,其後,只要至少還有一個讀者在讀數據,讀者就一直保持對數據區的控制權,這會造成寫者的"飢餓";

    b)問題二:用AND信號量解第一類讀寫問題

begin
               var L,mx:semaphore = RN,1;//最多只能有RN個讀者同時讀數據         
                cobegin

                    process Reader
                    begin
                       repeat
                        Swait(L,1,1);//L》1表示讀者數量不滿RN個,此讀者可以讀
                       Swait(mx,1,0);//mx》1表示沒有寫者在寫數據,此讀者可讀
             讀數據
                       Ssignal(L,1);
        until false
                    end

                    process Writer
                    begin
                        repeat
                      Swait(mx,1,1;L,RN,0);//mx》1表示沒有寫者在寫數據,同時L》RN表示沒有讀者在讀數據
            寫數據
                       Ssignal(mx,1);
         until false
                    end
    coend
  end 

    c)第二類讀寫問題   

begin
               var Rmutex,WRmutex:semaphore,wx= 1,1,1;
                readcount:integer=0;
                cobegin

                    process Reader
                    begin
                       repeat
          wait(wx)
                        wait(Rmutex)
                       readcount = readcount+1;
                      if(readcount==1) wait(WRmutex);//第一個讀者開始讀數據,禁止寫者訪問
                       signal(Rmutex)
             讀數據
                        wait(Rmutex)
          signal(wx)
                       readcount = readcount-1;
                      if(readcount==0) signal(WRmutex);//沒有讀者讀數據喚醒寫者訪問
                       signal(Rmutex)
        until false
                    end

                    process Writer
                    begin
                        repeat
             wait(wx)
                        wait(WRmutex)
                         寫數據
                        signal(WRmutex)
            signal(wx)
         until false
                    end
    coend
  end 

3)哲學家進餐問題
   
    a)使用記錄型信號量

begin
               var chopstick[5]:integer = {1,1,1,1,1};
                    process Philosopher-i(i=1,25)
                    begin
                       repeat
                        wait(chopstick[i])
                        wait(chopstick[(i+1)mod5])
                             吃飯
                        signal(chopstick[i])
                        signal(chopstick[(i+1)mod5])
                            思考
            until false
                    end         
  end 

    b)使用AND信號量

begin
            var chopstick[5]:integer = {1,1,1,1,1}
                   process Philosopher-i(i=1,25)
                    begin
                       repeat
                   思考
                Swait(chopstick[i],chopstick[(i+1)mod5])
             吃飯
             Ssignal(chopstick[i],chopstick[(i+1)mod5])    
                until false
                    end         
  end 

4.真題精選

[復旦2000]
問答:     在計算機科學文獻中的幾種互斥算法中,所謂的Lamport麪包店算法可以有效地用於多個相互競爭的控制線程,該算法中線程之間的通信只能在共享內存中進行(即,不需要諸如信號量、原子性的set-and-test之類的專門機制)。該算法的基本思想源於麪包店,因爲麪包店需要先取號然後等候叫號。下面給出了該算法的框架,該算法可以使各線程進出臨界區而不產生衝突。

// declaration & initial values of global variables
Enter, Number: array [1..N] of integer = {0};

// logic used by each thread
// where "(a, b) < (c, d)"
// means "(a < c) or ((a == c) and (b < d))"
Thread(i) {
  while (true{
    Enter [i] = 1;
    Number[i] = 1 + max(Number[1],,Number[N]);
    Enter [i] = 0;
    for (j=1; j<=N; ++j) {
      while (Enter[j] != 0) {
        // wait until thread j receives its number
      }

      while ((Number[j]!=0)
         && ((Number[j],j) < (Number[i],i))) {
        // wait until threads with smaller numbers
        
// or with the same number, but with higher
        
// priority, finish their work
      }

    }

    // critical section
    Number[i] = 0;
    // non-critical section
  }

}

定義(a,b)<(c,d)爲(a<c) or (a=c and b<d);
Enter[i]和Number[i]可以被進程i讀寫,但是隻能被進程j(j<>i)讀.
請問:(1)算法如何解決互斥訪問臨界區問題 
        (2) 算法如何解決死鎖問題
分析:每個進入麪包店的顧客會得到一個號碼,服務員爲擁有最小號碼的顧客服務.此算法需要進程pi經過兩個步驟進入臨界區.第一步,它需要選擇一個號碼,因此,它讀取其他進程的號碼,並選擇一個比它們的最大值大1的數字作爲自己的號碼(稱爲"麪包店入口).第二步,進程pi用如下方法檢查是否可以進入臨界區.對任意其他進程pj,pi首先檢查pj是否已經在麪包店入口,如果在,那麼pi等待pj離開麪包店入口,然後pi等待Number[j]變爲0或者Number[j],j) < (Number[i],i).當pi對所有其他進程成功驗證上述條件後,就可以進入臨界區.

解:
首先證明兩個結論:
    (1)若進程pi已經在臨界區,而且若干其他進程pk已經選擇了他們的號碼,那麼(Number[i],i) < (Number[k],k).
        證明:若pi已經在臨界區,則一定經歷了k輪for循環,就是說在上述幾輪循環中,Number[k]=0或者(Number[i],i) < (Number[k],k).首先假設進程pi讀出Number[k]爲0,也就是說pk還沒有選擇好一個號碼,這裏有兩種情形:一是pk不在麪包店入口,二是已經進入但還沒有退出.若pk不在麪包店入口,則它將可以讀到最新的Number[i]的值,從而確保Number[k]>Number[i].若它已經在入口處,則此次進入一定是在pi檢查了Enter[k]之後,因爲pi在檢查條件(Number[k]==0)  && ((Number[i],i) < (Number[k],k))) 之前需要等待pk完成選擇,也就是說pk將讀到最新的Number[i]的值,因此確保Number[k]>Number[i].如果在k輪循環中,(Number[i],i) < (Number[k],k).那麼這個結論將繼續保持,因爲Number[i]的值不會改變,而Number[k]只會增加.
    (2)若進程pi在臨界區中,則Number[i]>0
        證明:因爲此數值至少爲0,當一個進程進入臨界區之前一定執行了一個加1的操作.
    由上述結論,若有兩個進程pi和pk同時進入了臨界區,由(2)知它們的號碼都大於0,由(1),則有Number[k]>Number[i],反之也一樣,所以矛盾.
    對於死鎖問題,由上述結論知,任一時刻一定存在一個進程pi,它的(Number[i],i)值最小,因此它總能夠執行完成並歸還資源,所以不會產生死鎖.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章