信号量 进程 (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
 type semaphore = record value:integer
            value:integer L: list of process;
            L: list of process; end
    end procedure wait(s)
    procedure wait(s) var s: semaphore;
        var s: semaphore; begin
            begin s.value = s.value - 1;
                s.value = s.value - 1; if(s.value<0) then block(s.L)
                if(s.value<0) then block(s.L) end
            end procedure signal(s)
    procedure signal(s) var s: semaphore
        var s: semaphore begin
            begin s.value = s.value +1;
                s.value = s.value +1; if(s.value<=0) then wakeup(s.L)
                if(s.value<=0) then wakeup(s.L) end
            end
   结论:
    (1) 若信号量s为正值,则等于在封锁进程之前对信号梁可以施行的p操作数,也就是s所代表的实际可以使用的物理资源数.
    (2) 若信号量s为负值,则其绝对值等于排列在该信号量s队列之中等待的进程个数,也就等于对信号量s实施p操作而被封锁起来并进入信号量s队列的进程数.
    (3)p操作一般代表请求一个资源,v操作一般代表着释放一个资源,在一定条件下,p操作代表挂起进程操作,v操作代表唤醒被挂起的进程.
    3.经典同步问题
    1)生产者/消费者问题
           a) 问题一:有一个生产者和一个消费者,共享一个缓冲区.两者要互斥访问缓冲区.
        解:定义两个信号量:empty表示缓冲是否为空,初值为1,即初始时可以存入一件物品.full表示缓冲区中是否有物品,初值为0,即初始时缓冲区没有物品.
 程序:
            程序: begin
                    begin  buffer:integer
                        buffer:integer empty,full:semaphore=1,0;
                        empty,full:semaphore=1,0; cobegin
                        cobegin process Producer
                        process Producer begin
                            begin L1:生产一件物品
                                L1:生产一件物品 wait(empty);
                                wait(empty); buffer = product;
                                 buffer = product; signal(full);
                                 signal(full); goto L1;
                                 goto L1; end
                            end process Consumer
                        process Consumer begin
                            begin L2:wait(full);
                                L2:wait(full); 从缓冲区取出一件物品;
                                从缓冲区取出一件物品; signal(empty);
                                signal(empty); 消费掉物品
                                消费掉物品 goto L2;
                                goto L2; end
                           end coend
                        coend end
                    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
            begin buffer:array[0
                buffer:array[0 r-1]: integer
r-1]: integer in,out:integer=0,0;
                in,out:integer=0,0; empty=r,full=0,mutex1=1,mutex2=1:semaphore;
                empty=r,full=0,mutex1=1,mutex2=1:semaphore; cobegin
                cobegin process Producer-i(i=1,2
                    process Producer-i(i=1,2 m)
m) begin
                    begin L1:生产一件物品;
                        L1:生产一件物品; wait(empty)
                        wait(empty) wait(mutex1)
                        wait(mutex1) buffer[in] = product;
                         buffer[in] = product; in = (in+1)mod r;
                         in = (in+1)mod r; signal(mutex1)
                        signal(mutex1) signal(full)
                        signal(full) goto L1;
                        goto L1; end
                    end process Consumer-j(j=1,2
                    process Consumer-j(j=1,2 n)
n) begin
                    begin L2: wait(full)
                        L2: wait(full) wait(mutex2)
                        wait(mutex2) take  a product from buffer[out];
                        take  a product from buffer[out]; out =(out+1)mod r;
                        out =(out+1)mod r; signal(mutex2)
                        signal(mutex2) signal(empty)
                        signal(empty) 消费一件物品;
                        消费一件物品; goto L2;
                        goto L2; end
                    end
    c)问题三:m个生产者,n个消费者,容量为r的缓冲区,(m,n,r都大于1),要求生产者和消费者互斥存取物品.
       解:1)生产者和消费者之间要同步,类似问题一,用两个信号量empty,full;2)m个生产者之间要互斥,n个消费者之间也要互斥,同时要求生产者和消费者互斥存取物品,因此设两个计数器in,out和一个互斥信号量mutex.
代码:
 begin
begin buffer:array[0
                buffer:array[0 r-1]: integer
r-1]: integer in,out:integer=0,0;
                in,out:integer=0,0; empty=r,full=0,mutex=1:semaphore;
                empty=r,full=0,mutex=1:semaphore; cobegin
                cobegin process Producer-i(i=1,2
                    process Producer-i(i=1,2 m)
m) begin
                    begin L1:生产一件物品;
                        L1:生产一件物品; wait(empty)
                        wait(empty) wait(mutex)
                        wait(mutex) buffer[in] = product;
                         buffer[in] = product; in = (in+1)mod r;
                         in = (in+1)mod r; signal(mutex)
                        signal(mutex) signal(full)
                        signal(full) goto L1;
                        goto L1; end
                    end process Consumer-j(j=1,2
                    process Consumer-j(j=1,2 n)
n) begin
                    begin L2: wait(full)
                        L2: wait(full) wait(mutex)
                        wait(mutex) take  a product from buffer[out];
                        take  a product from buffer[out]; out =(out+1)mod r;
                        out =(out+1)mod r; signal(mutex)
                        signal(mutex) signal(empty)
                        signal(empty) 消费一件物品;
                        消费一件物品; goto L2;
                        goto L2; end
                    end
注:应该先执行对资源信号量的p,v操作,再执行互斥信号量的p,v操作,否则会引起死锁.
2)读者/写者问题
    要求:(1)允许多个读者同时从数据区读数据;
            (2)当读者在读数据时,不允许写者写数据
            (3)任何时候只允许一个写者向数据区写数据;
            (4)若有写者在写数据,则不允许读者读数据.
问题分为读者优先和写者优先两类。若有读者在读数据时,写者到来时就会被阻塞,直到所有读者读完数据才被唤醒.这里读者优先于写者,称为第一类读写问题.若读者在读数据时,有写者到来,则后续的读者将被阻塞,直到写者离开才被唤醒,这类问题中写者处于优势地位,叫第二类读写问题.
    a)问题一:用记录型信号量解第一类读写问题
 begin
begin var Rmutex,WRmutex:semaphore = 1,1;
               var Rmutex,WRmutex:semaphore = 1,1; readcount:integer=0;
                readcount:integer=0; cobegin
                cobegin
 process Reader
                    process Reader begin
                    begin repeat
                       repeat wait(Rmutex)
                        wait(Rmutex) readcount = readcount+1;
                       readcount = readcount+1; if(readcount==1) wait(WRmutex);//第一个读者开始读数据,禁止写者访问
                      if(readcount==1) wait(WRmutex);//第一个读者开始读数据,禁止写者访问 signal(Rmutex)
                       signal(Rmutex) 读数据
             读数据 wait(Rmutex)
                        wait(Rmutex) readcount = readcount-1;
                       readcount = readcount-1; if(readcount==0) signal(WRmutex);//没有读者读数据唤醒写者访问
                      if(readcount==0) signal(WRmutex);//没有读者读数据唤醒写者访问 signal(Rmutex)
                       signal(Rmutex) until false
        until false end
                    end
 process Writer
                    process Writer begin
                    begin repeat
                        repeat wait(WRmutex)
                        wait(WRmutex) 写数据
                         写数据 signal(WRmutex)
                        signal(WRmutex) until false
         until false end
                    end coend
    coend end
  end 注:
    (1)WRmutex用于读者和写者,写者和写者之间互斥访问数据区.
    (2)当没有读者访问时,允许一个读者进入,第一个读者进入时要用WRmutex与写者互斥.
    (3) readcount记录正在读数据区的读者数量,由于能被多个读者进程共享,也是临界资源,因此用Rmutex来对其实施互斥访问.
    (4)允许多个读者同时读数据,当至少有一个读者在读时,其他读者不用等待就可以读数据。
    (5)一旦有一个读者开始读数据,其后,只要至少还有一个读者在读数据,读者就一直保持对数据区的控制权,这会造成写者的"饥饿";
    b)问题二:用AND信号量解第一类读写问题
 begin
begin var L,mx:semaphore = RN,1;//最多只能有RN个读者同时读数据
               var L,mx:semaphore = RN,1;//最多只能有RN个读者同时读数据          cobegin
                cobegin
 process Reader
                    process Reader begin
                    begin repeat
                       repeat Swait(L,1,1);//L》1表示读者数量不满RN个,此读者可以读
                        Swait(L,1,1);//L》1表示读者数量不满RN个,此读者可以读 Swait(mx,1,0);//mx》1表示没有写者在写数据,此读者可读
                       Swait(mx,1,0);//mx》1表示没有写者在写数据,此读者可读 读数据
             读数据 Ssignal(L,1);
                       Ssignal(L,1); until false
        until false end
                    end
 process Writer
                    process Writer begin
                    begin repeat
                        repeat Swait(mx,1,1;L,RN,0);//mx》1表示没有写者在写数据,同时L》RN表示没有读者在读数据
                      Swait(mx,1,1;L,RN,0);//mx》1表示没有写者在写数据,同时L》RN表示没有读者在读数据 写数据
            写数据 Ssignal(mx,1);
                       Ssignal(mx,1); until false
         until false end
                    end coend
    coend end
  end 
c)第二类读写问题
 begin
begin var Rmutex,WRmutex:semaphore,wx= 1,1,1;
               var Rmutex,WRmutex:semaphore,wx= 1,1,1; readcount:integer=0;
                readcount:integer=0; cobegin
                cobegin
 process Reader
                    process Reader begin
                    begin repeat
                       repeat wait(wx)
          wait(wx) wait(Rmutex)
                        wait(Rmutex) readcount = readcount+1;
                       readcount = readcount+1; if(readcount==1) wait(WRmutex);//第一个读者开始读数据,禁止写者访问
                      if(readcount==1) wait(WRmutex);//第一个读者开始读数据,禁止写者访问 signal(Rmutex)
                       signal(Rmutex) 读数据
             读数据 wait(Rmutex)
                        wait(Rmutex) signal(wx)
          signal(wx) readcount = readcount-1;
                       readcount = readcount-1; if(readcount==0) signal(WRmutex);//没有读者读数据唤醒写者访问
                      if(readcount==0) signal(WRmutex);//没有读者读数据唤醒写者访问 signal(Rmutex)
                       signal(Rmutex) until false
        until false end
                    end
 process Writer
                    process Writer begin
                    begin repeat
                        repeat wait(wx)
             wait(wx) wait(WRmutex)
                        wait(WRmutex) 写数据
                         写数据 signal(WRmutex)
                        signal(WRmutex) signal(wx)
            signal(wx) until false
         until false end
                    end coend
    coend end
  end 3)哲学家进餐问题
   
    a)使用记录型信号量
 begin
begin var chopstick[5]:integer = {1,1,1,1,1};
               var chopstick[5]:integer = {1,1,1,1,1}; process Philosopher-i(i=1,2
                    process Philosopher-i(i=1,2 5)
5) begin
                    begin repeat
                       repeat wait(chopstick[i])
                        wait(chopstick[i]) wait(chopstick[(i+1)mod5])
                        wait(chopstick[(i+1)mod5]) 吃饭
                             吃饭 signal(chopstick[i])
                        signal(chopstick[i]) signal(chopstick[(i+1)mod5])
                        signal(chopstick[(i+1)mod5]) 思考
                            思考 until false
            until false end
                    end          end
  end b)使用AND信号量
 begin
begin var chopstick[5]:integer = {1,1,1,1,1};
            var chopstick[5]:integer = {1,1,1,1,1};  process Philosopher-i(i=1,25)
                   process Philosopher-i(i=1,25) begin
                    begin repeat
                       repeat 思考
                   思考 Swait(chopstick[i],chopstick[(i+1)mod5])
                Swait(chopstick[i],chopstick[(i+1)mod5]) 吃饭
             吃饭 Ssignal(chopstick[i],chopstick[(i+1)mod5])
             Ssignal(chopstick[i],chopstick[(i+1)mod5])     until false
                until false end
                    end          end
  end 
4.真题精选
[复旦2000]
问答:     在计算机科学文献中的几种互斥算法中,所谓的Lamport面包店算法可以有效地用于多个相互竞争的控制线程,该算法中线程之间的通信只能在共享内存中进行(即,不需要诸如信号量、原子性的set-and-test之类的专门机制)。该算法的基本思想源于面包店,因为面包店需要先取号然后等候叫号。下面给出了该算法的框架,该算法可以使各线程进出临界区而不产生冲突。
 // declaration & initial values of global variables
// declaration & initial values of global variables Enter, Number: array [1..N] of integer = {0};
Enter, Number: array [1..N] of integer = {0};
 // logic used by each thread
// logic used by each thread
 // where "(a, b) < (c, d)"
// where "(a, b) < (c, d)" // means "(a < c) or ((a == c) and (b < d))"
// means "(a < c) or ((a == c) and (b < d))" Thread(i) {
Thread(i) { while (true) {
  while (true) { Enter [i] = 1;
    Enter [i] = 1; Number[i] = 1 + max(Number[1],
    Number[i] = 1 + max(Number[1], ,Number[N]);
,Number[N]); Enter [i] = 0;
    Enter [i] = 0; for (j=1; j<=N; ++j) {
    for (j=1; j<=N; ++j) { while (Enter[j] != 0) {
      while (Enter[j] != 0) { // wait until thread j receives its number
        // wait until thread j receives its number }
      } while ((Number[j]!=0)
      while ((Number[j]!=0) && ((Number[j],j) < (Number[i],i))) {
         && ((Number[j],j) < (Number[i],i))) { // wait until threads with smaller numbers
        // wait until threads with smaller numbers // or with the same number, but with higher
        // or with the same number, but with higher // priority, finish their work
        // priority, finish their work }
      } }
    } // critical section
    // critical section
 Number[i] = 0;
    Number[i] = 0; // non-critical section
    // 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)值最小,因此它总能够执行完成并归还资源,所以不会产生死锁.
 
                    
                     
                    
                 
                    
                 var chopstick[
               var chopstick[ 
   
                
            
         
 
         浙公网安备 33010602011771号
浙公网安备 33010602011771号