操作系统原理:读写者经典同步问题

读者-写者问题的读写操作限制:

  • 写-写互斥,即不能有两个写者同时进行写操作。
  • 读-写互斥,即不能同时有一个线程在读,而另一个线程在写。
  • 读-读允许,即可以有一个或多个读者在读。

一、读者优先

当读者优先时,写文件的线程需要等读文件的线程执行完才可以执行,如果还有在读的线程则写线程需要等待。

理一理读者优先的互斥关系

1)当写文件的时候其他线程都不能访问,那么程序怎么知道有没有写线程在执行呢?所以需要一个信号量WriteMutex来记录允许同时写文件的线程数,即初始值(最大值)为1的信号量,同时也是互斥量、锁。当读和写线程的时候都需要检查WriteMutex的值。  WriteMutex保证了写-写和读-写互斥

Writer(){
  wait(WriteMutex);    //WriteMutex减1 
  write ....
  post(WriteMutex);    //WriteMutex加1 
}


Reader(){
  wait(WriteMutex);  
  write ....
  post(WriteMutex);
}

2)在步骤一实现的条件,下如何实现读者优先呢? 如果WriteMutex检查通过,意味着没有写任务,接下来执行的应全是读任务。需要思考的是,当有n的读线程队列有执行权时,此时来了一个写线程,怎么保证这n个读线程能够控制住 WriteMutex 不大于0 呢?

3) 读-读被允许,上述写法无法支持多个读线程并行。因为WriteMutex的最大值为1,被 wait() 一次值就为0了,再wait就阻塞了。如何支持多个读线程并行呢?只需要第一个读线程 wait(WriteMutex),最后一个读线程post(WriteMutex) 就可以。那么需要一个整形的 Rcount 变量来记录正在执行读操作的线程个数。Rcount是共享变量,且大于1。所有值可能大于1的共享变量都需要有一个互斥变量(锁),来保证访问改共享变量的互斥性。 所以Rcount需要一个互斥变量CountMutex ,具体的互斥操作就是,访问Rcount时,利用CountMutex将Rcount “包”起来。

Writer(){
  wait(WriteMutex);    //WriteMutex减1 
  write ....
  post(WriteMutex);    //WriteMutex加1 
}


Reader(){
  wait(CountMutex);    // 每次访问Rcount前都要保证互斥
  if(Rcount == 0 )     //没有在执行的读线程,意味着当前时读线程队列的第一个
    wait(WriteMutex);  // 第一个读线程保证不允许写操作。
  Rcount ++;
  post(CountMutex);    // 访问完Rcount就要释放访问权

  read ....

  wait(CountMutex); 
  Rcount --;
  if(Rcount == 0 )     //没有在执行的读线程,意味着当前时读线程队列的最后一个
    post(WriteMutex);
  post(CountMutex);

}

二、写者优先

     当写者优先时,读文件的线程需要等写文件的线程执行完才可以执行,如果还有在写任务则读线程需要等待。

     我们也理一理写者优先的互斥关系。上述的读者优先用信号量的方式实现。那么这次写着优先,打算用Hansen Style管程的方式实现。

    回想一下管程数据结构有啥?既然是管程那么就需要条件变量和管程锁,还有资源变量,还有等待访问管程的线程队列。 管程锁保证了最多只有1个线程在管程中执行。条件变量中包含了需要进行相应操作的阻塞队列和阻塞线程数。执行条件变量wait时,会释放管程锁,让线程进入阻塞态。如果被唤醒,那么再获取管程锁继续执行。执行条件变量signal时,会把阻塞线程队列唤醒1个线程。执行条件变量brocase

1)读写者问题中有两种类型的操作,一个是读操作和另一个写操作。所以条件变量需要两个,okToRead和okToWrite 来记录被阻塞的读线程和写线程数。一个管程只需要1个管程锁。那么条件变量 和  缓存区锁的设计可以了,资源变量又有什么呢?

已知阻塞写线程有 n 和 阻塞读线程有 m个。

2)当有写线程进入临界区时,其他线程不可以进入临界区。所以需要资源变量AW来记录在临界区的写文件的线程数,即初始值为0,最大值为1。当写线程退出临界区时,唤醒1个写线程,如果没有等待的写线程,则唤醒所有阻塞的读线程。所以需要有WR来记录被阻塞的读线程数,WW记录被阻塞的写线程数。

3)  允许有 m 个读线程同时进入临界区并于写线程互斥,那么就要像上面读者优先一样,用一个资源变量AR来记录在临界区的读文件的线程数。当中间有写线程待访问时必须等所有读线程都退出临界区才行

数据结构如下:

对于读操作:

1)获取管程锁后,利用AW和WW判断是否有正在写和等待写的任务,如果有则将自己阻塞到等待读队列中,并修改WR值

2)保证了不存在写线程,那么就进入临界区,AR ++ ,读共享资源。没有给read database 加锁,意味着所有的读线程都可以同时处于临界区中。

3)当访问完临界区,AR--时,利用AR 和 AW 判断,如果所有读线程都退出临界区,且存在阻塞的写线程时才唤醒写线程。

对于写操作:

1)获取管程锁后,利用AW和AR判断是否有正在写和正在读的任务,如果有则将自己阻塞到等待写队列中,并修改WW值。

2)保证了只有自己一个线程处于执行态,那么就进入临界区,AW ++ ,写共享资源。虽然没有给write database 加锁,但是前面保证了只有1个写线程可进入临界区就不需要加锁了。

3)当访问完临界区,AW--时,如果有等待的写线程就唤醒1个,没有等待的写线程就唤醒所有等待的读线程。

猜你喜欢

转载自blog.csdn.net/superSmart_Dong/article/details/116905485
今日推荐