二十、经典同步问题-读者写者问题

简介: 二十、经典同步问题-读者写者问题

1、读者-写者问题描述


动机: 共享数据的访问,假设有两种类型的使用者:一种为读者,不需要修改数据;另一种为写者,其会读取和修改数据。问题的约束包括: 允许在同一时间有多个读者,但在任何时候都只有一个写者;当没有写者时,读者才能访问数据;当没有读者和写者时,写者才能访问数据;在任何时候只能有一个线程可以操作共享变量;读者优先,若在读者前面有写者在排队等待,读者可以越过写者优先进行排队。


设计算法需要用到的共享数据: 包括数据集,信号量(CountMutex)初始化为1,信号量(WriteMutex)初始化为1,整数Rcount初始化为0(记录当前读者的个数)。



2、基于信号量实现


基于读者优先和信号量的操作逻辑如下所示:

//基于信号量的writer操作逻辑
sem_wait(WriteMutex);//P操作-1,保证同一时间只有一个进程可以进行写操作
  write;
sem_post(WriteMutex);//V操作+1
//基于信号量的Reader操作逻辑
sem_wait(CountMutex);//对Rcount进行保护,进行线程互斥
  if(Rcount == 0)
    sem_wait(WriteMutex);//P操作-1,保证同一时间只有一个进程可以进行写操作
  ++Rcount;
sem_post(CountMutex);
read;
--Rcount;
sem_wait(CountMutex);//对Rcount进行保护,进行线程互斥
  if(Rcount == 0)
    sem_post(WriteMutex);//V操作+1,当最后一个读者读完之后,释放线程锁,让写者进来
sem_post(CountMutex);




3、基于管程实现


基于写者优先和管程的操作逻辑如下所示:

//基于管程的reader操作逻辑伪代码如下所示
Database::Read()
{
  Wait until no writers;//这里读者需要等待正在临界区进行操作的写者和处于等待状态的写者
  read database;
  check out - wake up waiting writers;//唤醒处于等待状态的写者
}


//基于管程的writer操作逻辑
Database::Write()
{
  Wait until no readers/writers;//这里写者需要等待正在进行操作的读者或者写者
  write database;
  check out - wake up waiting readers/writers;//唤醒处于等待状态的读者
}


//管程中的条件变量包括以下
AR=0;//正在进行读操作的reader的个数
AW=0;//正在进行写操作的writer的个数
WR=0;//等待队列中处于等待状态的reader的个数
WW=0;//等待队列中处于等待状态的writer的个数
Condition okToRead;//条件变量,表示当前是否可以进行读操作了
Condition okToWrite;//条件变量,表示当前是否可以进行写操作了
Lock lock;
Public Database::Read()
{
  //管程实现reader
  StartRead();//Wait until no writers
  read databasel
  DoneRead();//check out - wake up waiting writers
}
Private Database::StartRead()
{
  lock.Acquire();//确保只有一个进程进入到管程之中
  while(AW+WW>0) //若有正在等待或者正在进行写操作的writer
  {
    ++WR;
    okToRead.wait(&lock);
    --WR;
  }
  ++AR;
  lock.Release();
}
Private Database::DoneRead()
{
  lock.Acquire();//确保只有一个进程进入到管程之中
  --AR;
  if(AR == 0 && WW > 0)//若当前没有正在进行读操作的reader了同时有正在等待的writer
  {
    okToWrite.signal();//唤醒一个正在等待的writer
  }
  lock.Release();
}
Public Database::Write()
{
  //管程实现Writer
  StartWrite();//Wait until no readers/writers
  read databasel
  DoneWrite();//check out - wake up waiting readers/writers
}
Private Database::StartWrite()
{
  lock.Acquire();//确保只有一个进程进入到管程之中
  while(AR+AW>0) //若有正在进行读操作的reader或者正在进行写操作的writer
  {
    ++WW;
    okToWrite.wait(&lock);
    --WW;
  }
  ++AW;
  lock.Release();
}
Private Database::DoneWrite()
{
  lock.Acquire();//确保只有一个进程进入到管程之中
  --AW;
  if(WW > 0)//若当前有正在等待的writer
    okToWrite.signal();//唤醒一个正在等待的writer
  else if(WR > 0)//若当前有正在等待的reader
    okToRead.broadcast();//唤醒等待在条件变量上的所有的reader
  lock.Release();
}





相关文章
|
存储 Java 数据库
一篇搞懂Java多线程(全网最细)(三)
一篇搞懂Java多线程(全网最细)(三)
56 0
|
Java API 调度
一篇搞懂Java多线程(全网最细)(二)
一篇搞懂Java多线程(全网最细)(二)
53 0
|
安全 网络协议 Java
一篇搞懂Java多线程(全网最细)(一)
一篇搞懂Java多线程(全网最细)(一)
72 0
|
Linux
linux系统编程(十一)线程同步(完结)(下)
linux系统编程(十一)线程同步(完结)
210 0
linux系统编程(十一)线程同步(完结)(下)
|
Linux 调度 数据库
linux系统编程(十一)线程同步(完结)(上)
linux系统编程(十一)线程同步(完结)
178 0
linux系统编程(十一)线程同步(完结)(上)
|
数据采集 算法 数据库
库调多了,都忘了最基础的概念-《死锁与范式的碰撞》
库调多了,都忘了最基础的概念-《死锁与范式的碰撞》
106 0
库调多了,都忘了最基础的概念-《死锁与范式的碰撞》
|
存储 安全 Java
(十六)关于Java多线程锁的升级原理,这篇文章会让你另有收获
对象头用于存储对象的元数据信息,包括运行时数据和类型指针、实例数据存储的是真正有效数据、对齐填充主要补充字节,使得内存所占字节能被8整除。
|
安全 C++
模拟生产者-消费者问题和读者-写者问题
模拟生产者-消费者问题和读者-写者问题
406 0
模拟生产者-消费者问题和读者-写者问题
C#基础知识学习之 ☀️ | 多线程的使用基础
C#多线程 多线程概念 线程在程序中经常被用到,现在的计算机都是可以异步执行很多操作的,所以多线程的作用可见一斑! 线程 被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作。 线程是轻量级进程。一个使用线程的常见实例是现代操作系统中并行编程的实现。使用线程节省了 CPU 周期的浪费,同时提高了应用程序的效率。 到目前为止我们编写的程序是一个单线程作为应用程序的运行实例的单一的过程运行的。 但是,这样子应用程序同时只能执行一个任务。为了同时执行多个任务,它可以被划分为更小的线程
C#基础知识学习之 ☀️ | 多线程的使用基础
|
前端开发 Java 开发工具
学习java多线程,这必须搞懂的这几个概念,很重要。
同步,Synchronous,即调用方法开始,一旦调用就必须等待方法执行完返回才能继续下面的操作。
93 0