读者-写者问题

该篇将详细解析读者-写者问题,附加一道类似题加深了解。

问题描述:

      一个数据问价或记录可以被多个进程共享,我们把只读该文件的进程称为“读者进程”,其他进程为“写者进程”。允许多个进程同时读一个共享对象,但不允许一个写者进程和其他写者进程或读者进程同时访问共享对象。即:保证一个写者进程必须与其他进程互斥的访问共享对象的同步问题:

  1. 允许多个读者同时执行读操作。
  2. 不允许读者、写者同时操作。
  3. 不允许多个写者同时操作。

问题分析:

       一共两个进程,分别是writer()进程和read()进程,还需要两个互斥信号量,分别实现对读进程和写进程的互斥访问,即rw表示读写进程的互斥且只允许一个写进程访问临界区和re信号量实现允许多个读者访问临界区。

       但是当读进程连续访问临界区进行操作时,要确保写进程不能插队。因为读进程可以连续,为了防止写进程在读进程还未连续访问结束时插队访问,那么就需要在第一个读进程进来时,把写进程锁住,直到最后一个读进程离开临界区时,就释放掉对写进程的访问。要实现这个步骤,就需要count信号量来确保当前读者是第几个。

     初始化count=0,表示第一个读者进来时,count=0;第二个读者进来时,count就变为1,则不符合if里的判断,直接跳过对if操作,从而实现第一个读者执行if操作里的锁存写。解锁写操作同理。

伪代码:

//定义信号量
semaphore rw = 1;  //用于实现对缓冲区的互斥访问,只允许一个写进程访问
semaphore rea = 1      //对读进程上锁,即允许其他读进程访问,但不允许写进程访问
int  count = 0;  // 记录当前有几个读进程在访问文件,0代表只有1个读进程
 //写者进程
writer(){
   while(1){
       
       P(rw); //写之前"加锁",实现写操作单一执行。
       写文件;
       V(rw);  //写之后"解锁"
    
          }
      }  
 
//读者进程
reader(){
  while(1){
     p(rea);//进入读进程访问区
 //判断该读进程是否为第一个,是的话则开启对写进程的上锁
       if(count==0){
           P(rw);  //第一个读进程对写进程进行加锁
                    }
          count++;   //读进程的连续进入的计数
      V(rea); //释放对读进程的count++也就是读进程的访问
   
        读文件;
 //接下来进行判断count数量,判断是否读进程是否为最后一个
      P(rea); //  进入读进程访问区     
 //各进程互斥访问count,判断count数量,通过--,判断是否为最后一个数,是的话则释放对写进程的锁
      count--;          // 访问文件的读进程数-1
         if(count == 0) {    
            V(rw); //最后一个读进程负责对写进程解锁
                         }      
      V(rea);//释放读进程
         }
     }

下面提供一道与读者、学者问题类似的题加深印象。

问题描述:

      有桥如下图所示,车流方向如箭头所示,回答如下问题:
1)假设桥上每次只能有一辆车行驶,试用信号灯的P,V操作实现交通管理,
2)假设桥上不允许两车交会,但允许同方向多辆车一次通过(即桥上可有多辆同方向
行驶的车),试用信号灯的P,V操作实现桥上的交通管理。

问题分析:

     1)每次只能有一辆车行驶,所以只要设置一个信号量bridge就可判断桥是否使用,若在使用中,等待;若无人使用,则执行P操作进入:出桥后,执行V操作。

     2)桥上可以同方向多车行驶,需要设置 bridge,还需要对同方向车辆计数。为了防止同方向
计数中同时申请bridge造成同方向不可同时行车的问题,要对计数过程加以保护,因此设置信号量mutexSN和 mutexNS.

伪代码:

第一问
//信号量定义
semaphore bridge=1:	//用于互斥地访问桥	
    Ntos(){	//从北向南	
        P(bridge);
         通过桥;
        V(bridge);
      }
     StoN(){	//从南向北	
        P(bridge);
         通过桥:
       V(bridge);
     }





第二问
//信号量定义
int countSN=0;	//用于表示从南到北的汽车数量	
int countnNS=0;	//用于表示从北到南的汽车数量	
semaphore mutenSN=1;	//用于保护countSN	
semaphore mutexNS=1;	//用于保护countNS	
semaphore bridge=1;	    //用于互斥地访问桥	
  StoN(){	//从南向北	
         P(mutexSN);   //保护countSN,即车辆可以连续进入
       if (countSN==0)
        P(bridge);     //锁住桥这个临界资源
        countSN++;
        V(mutexSN);
          过桥;
       P(mutexSN)
       countsn--;
      if(countSN==0)
        V(bridge);    //所有车辆过完,释放桥的临界资源
        V(mutexSN);
}

  NtoS(){	//从北向南	
       P(mutexNS);    //保护countNS,即车辆可以连续进入
      if(countNS==0)
       P(brIdge);     //锁住桥这个临界资源
      countNS++;
       V(mutexNS);
        过桥;
      P(mutexNS);
      countns--;
     if(countNS==0)
      V(bridge);     //所有车辆过完,释放桥的临界资源
     V(mutexNS);
}

总结:遇到连续问题,都需要用到count进行计数,当count=0时,需要互斥信号量来锁住缓冲区,进行与其他进程互斥;同时为了防止在产品连续生产过程中有其他进程的干扰,需要单独再设置互斥信号量来保护count(以便于可以连续加减),比如读者写者问题中的p(rea)和过桥问题中的p(mutexSN),p(mutexNS)。

其他进程同步问题以及一些真题练习请看我的其他博客~~

Logo

鸿蒙生态一站式服务平台。

更多推荐