进程同步中的读者写者问题

读者写者问题-读者优先,读写公平,写者优先

本文发表于我的个人博客:进程同步中的读者写者问题

问题描述

有读者和写者两组并发进程,共享一个文档,因此要求:

  • 允许多个读者同时读
  • 只允许一个写者往文件中写信息
  • 任意一个写者在完成写操作之前不允许其他读者或写者工作
  • 写者写操作前,应该让已有的读者和写者全部退出

读者优先(写者可能饿死)

这部分比较简单,一般课件上都会有

使用count来记录读者的数量,mutex保护count变量, rw保证读者写者互斥访问文件

int count = 0;
Semaphore mutex = 1; // count 变量保护
Semaphore rw = 1; // 文件保护
void writer(){
  while(true){
    P(rw);
    // writing 开始写
    V(rw); // 写完了释放临界区
  }
}

void reader(){
  while(true){
    P(mutex);
    if (count == 0)	// 如果是第一个读者进入,那么开始占用文件
      P(rw);
    count++;
    V(mutex);
    // reading 开始读
    P(mutex);
    count--;
    if (count == 0) // 如果是最后一个读者离开,释放文件占用
      V(rw);
    V(mutex);
  }
}

由此可见,上面这个算法中,若源源不断地有读者进来,count会一直不为0,导致写者没有办法进入临界区,一直被P(rw)阻塞在外面卡住了。

读写公平

希望能读者、写者先来后到按照顺序进行,在上面的基础上,当有写进程请求访问,应当禁止后续的读进程的请求,等到已在共享文件的读进程执行完毕,立即让写进程执行。

在没有写者的情况下,才会让读者再次运行。

为此,在读者优先的基础上,增加一个信号量q的PV操作,对读者、写者进行约束。

其中,P(q)相当于开始排队,V(q)相当于排队的人走了唤醒下一个队列首的人。

int count = 0;
Semaphore mutex = 1; // count 变量保护
Semaphore rw = 1; // 文件保护
Semaphore q = 1; // 使得writer和reader在同一个队列排队
void writer(){
  while(true){
    P(q);	// 无写进程时请求进入
      P(rw);
      // writing 正在写
      V(rw); // 写完了释放临界区
    P(q);	// 恢复对共享文件的访问
  }
}

void reader(){
  while(true){
    P(q);	// 这一步使得reader有机会让给writer写 
      P(mutex);
      if (count == 0)	// 如果是第一个读者进入,那么开始占用文件
        P(rw);
      count++;
      V(mutex);
    V(q);	// 这一步唤醒被阻塞在q队列的第一个进程
    
    // reading 正在读
    
    P(mutex);
    count--;
    if (count == 0) // 如果是最后一个读者离开,释放文件占用
      V(rw);
    V(mutex);
  }
}

写者优先问题

  • 由于读-写、写-写互斥,使用信号量file控制临界区访问

    • 一群读者在读取时若有写者进来,会在队列占有quemutex,等待最后一个正在读的人离开释放file,然后立刻开始写
  • 使用一个readcount整型变量计数器,记录读者数量,用信号量rdcntmutex控制该变量的访问;writecount记录写者数量,需要信号量wrtcntmutex控制该变量访问

  • 为了实现写者优先,那么不能按照读者写者来的先后顺序排队,故设置两个队列,优先队列quemutex和读者等待队列readEntry

    • 当一个写者来时占用优先队列quemutex,一直占有到最后一个写者离开才能释放
    • 当一个读者来时放到readEntry排队,若此时前面没有其他读者,也没有写者,它才可以进到quemutex"就绪区",quemutex仅允许一个读者存在
    • 后续所有的读进程要统一放到readEntry等待。
    • 直到所有的写进程都写完了,才会唤醒quemutex中的唯一读者,该读者完成任务后,才会唤醒readEntry的第一个读者进入quemutex(即quemutex队列长度最多为2)
int readcount = 0, writecount = 0;
Semaphore file = 1, rdcntmutex = 1, wrtcntmutex = 1;
Semaphore quemutex = 1, readEntry = 1;
void Reader(){
  P(readEntry);
  	P(quemutex);			// 若有写者占用que,后续读者会被卡在外面
  		P(rdcntmutex);
  		if (readcount == 0) 
  			P(file);		// 若是第一个读者进来,则占据临界区阻止写进程写
  		readcount++;
  		V(rdcntmutex);
  	V(quemutex);
  V(readEntry);
  // 读操作不用互斥保护
  // reading operation
  // 读完了,读者走人
  P(rdcntmutex);
  	if (readcount == 1) 
  		V(file);		// 若是最后一个读者离开,则释放写进程的阻塞,允许写
  	readcount--;
  V(rdcntmutex);
}

void Writer(){
  P(wrtcntmutex);
    if (writecount == 0)
       P(quemutex);	// 一旦写者进入,则占用该优先队列
  	writecount++;
  V(wrtcntmutex);
  P(file);	// 等待现存读者读完唤醒阻塞的写者
  	// writing, 写进程需要互斥保护
  V(file);
  P(wrtcntmutex);
    if (writecount == 1)
       V(quemutex);// 若源源不断有写者进入,writecount不会为0,也不会释放
  	writecount--;		// 当最后一个写者离开,才会唤醒等在que的一个读者
  V(wrtcntmutex);
}
posted @ 2020-04-27 10:39  Vanellope  阅读(323)  评论(0编辑  收藏  举报