四、操作系统——读者写者问题(详解)

一、问题描述:

在这里插入图片描述

二、需要满足的条件:

  1. 写进程与写进程之间必须互斥的写入数据(因为如果两个写进程同时对共享数据中的区域A中的数据进行写操作的话,会导致数据错误覆盖的问题
  2. 写进程与读进程之间必须互斥的访问共享数据(因为写进程与读进程如果同时访问共享数据,可能会导致数据不一致的问题。比如:读进程A想要访问共享数据中的B数据,但是写进程C在读进程A访问B数据之前将B数据进行了更新,这就会导致读进程A读不到它想要读到的数据,从而出现数据不一致问题
  3. 读进程与读进程之间可以同时访问数据,不需要实现互斥的访问共享数据(因为读进程读数据,并不会像之前的生产者消费者问题中的消费者那样改变数据或者是将数据清空,所以多个读进程可以同时访问共享数据)

三、解题思路:

  1. 第一步解决写进程与写进程之间必须互斥的写入数据写进程与读进程之间必须互斥的访问共享数据 两个问题。

在这里插入图片描述

上面这种实现方式确实解决了了写进程与写进程之间必须互斥的写入数据写进程与读进程之间必须互斥的访问共享数据 这两个问题。但是,
假设读进程A正在访问共享数据,执行了P(rw) 和 读数据操作,还没有执行V操作解锁,此时读进程B也想访问共享数据,此时,读进程B会卡在P(rw)中的循环里面,也就是说进程B被阻塞了。
读进程与读进程之间也变成了必须互斥访问共享数据,并不满足题目读进程与读进程可以同时访问共享数据的要求

  1. 下面来实现多个读进程可以同时访问共享数据
    解决方法:
    引入count变量,用来记录当前有几个读进程在访问共享数据。

在这里插入图片描述

  1. 上述实现方法表面上看达到了实现多个读进程可以同时访问共享数据的目的,但其实还是存在问题的。
    存在的问题:
    如果读进程A想要访问共享数据,并且执行了P(rw)“上锁”操作,此时,读进程B也想要访问共享数据,也会执行P(rw),但是因为进程A已经执行了“上锁”操作,所以进程B还是会被阻塞,无法访问共享数据。可见,仍然没有达到多个读进程可以同时访问共享数据的目的!
    出现这种问题的原因:
    对于count变量的检查与赋值操作无法“一气呵成”,可以被中断。
    解决方法:
    可以增加一个mutex互斥信号量来保证if判断语句 和 count++(count–) 能够“一气呵成”执行完,中间不会被打断,保证各进程对count的访问是互斥的
    在这里插入图片描述
    在这里插入图片描述

  2. 上述解决方案确实已经达到了多个读进程可以同时访问共享数据的目的,但此时,又出现了新的问题:只要又读进程在读取共享数据,写进程就要一直阻塞等待,这很可能导致写进程一直无法往共享数据中写入数据,也就是说写进程很有可能会被“饿死”。因此,这种算法中,读进程是优先的!下面我们来解决写进程可能会被“饿死”这个问题
    解决方法:
    设置变量semaphore w =1 ,用于实现“写优先”。然后分别为读进程、写进程增加关于信号变量w的P、V操作。
    在这里插入图片描述
    在这里插入图片描述
    经过上面的“改造”,我们来验证一下是否达到了“写优先”的目的:
    读进程A——>写进程a——>读进程B:
    假设读进程A正在访问共享数据,那么读进程A肯定已经执行了P(w)、P(mutex)、P(rw)、V(mutex)、V(w)。此时,写进程a也想要访问共享数据,那么当读进程a执行P(w)时,不会被阻塞,但是执行到P(rw)时,由于读进程A还没有执行V(rw)“解锁”操作,所以,写进程a会被阻塞等待
    而如果此时有第二个读进程B也想要访问共享数据,但由于之前第一个读进程A已经执行了P(w)“上锁”操作,所以当读进程B执行到P(w)操作时,也会被堵塞等待
    直到读进程A完成了读文件操作后,执行了V(rw)“解锁”操作,写进程a才会被“唤醒”。然后在写进程完成了写文件操作后,执行了V(w)“解锁”操作,读进程B才能被唤醒
    注意:这里为什么会先唤醒写进程a呢?
    答:因为这里是写进程a比读进程B先想要访问共享数据,所以优先被唤醒。这里其实就是“先来先服务算法

结论:
在这种算法中,连续进入的多个读进程,可以同时读文件;写进程和其他进程不能同时访问文件;写进程不会“饥饿”。但也并不是真正的“写优先”,而是遵循相对公平的先来先服务原则。有的也称这种算法为“读写公平法”。

四、总结:

读者-写者问题为我们解决复杂的互斥问题提供了一个参考思路:
核心思想在于设置了一个计数器count用来记录当前正在访问共享文件的读进程数。我们可以用count的值来判断当前进入的进程是否是第一个/最后一个读进程,从而做出不同的处理。如果是第一个进程则执行“上锁”操作,如果是最后一个进程则执行“解锁”操作。
另外,对count变量的检查和赋值不能一气呵成导致了一些错误,如果需要实现“一气呵成”,自然应该想到用互斥信号量
最后,还要认真体会我们是如何解决“写进程饥饿”问题的。
绝大多数的考研PV操作大题都可以用之前介绍的几种生产者-消费者问题的思想来解决,如果遇到更复杂的问题,可以想想能否用读者写者问题的这几个思想来解决。

猜你喜欢

转载自blog.csdn.net/weixin_44827418/article/details/106195361