操作系统:读者写者问题

问题描述

  • 共享数据的两类使用者:
    • 读者:只读取数据,不修改
    • 写者:读取和修改数据
  • 需要满足的条件
    • 读读允许:同一时刻,允许有多个读者同时读
    • 读写互斥:没有写者时读者才能读,没有读者时写者才能写
    • 写写互斥:没有其他写者时写者才能写

信号量描述

  • 信号量 WriteMutex:
    • 控制读写操作的互斥
    • 初始化为1
  • 读者计数Rcount:
    • 正在进行读操作的读者数目
    • 初始化为0
  • 信号量 CountMutex
    • 控制对读者计数的互斥修改
    • 初始化为1

信号量是个整数变量,除了初始化外,它只能通过两个标准原子操作:wait()signal()来访问。这些操作原来被称为p (荷兰语proberen,测试)和v (荷兰语verhagen,增加);

信号量的定义为:

class Semaphore {
    int sem;
    WaitQueue q;
}

P() 的定义可表示为:

Semaphore::() {
   sem--;
   if (sem < 0) {
        Add this thread t to q;
        block(p);
     }
}

V()的定义可表示为:

Semaphore::V() {
    sem++; 
    if (sem <=0) {
        Remove a thread t from q;
        wakeup(t);        
    }
}

读者优先

首先,对读过程和写过程需要互斥访问,因此,我们有:

写者进程:

P(WriteMutex);
    write;  // 临界区
V(WriteMutex);

读者进程:

P(WriteMutex);
    read;  // 临界区
V(WriteMutex);

但是,我们有读读允许,只有第一个读者进来的时候需要和写者进行互斥,进行P()操作,第二个读者进来的时候就不需要进行P()操作了,因此,我们改写读者进程为:

//在读者进来的时候进行计数判断
if(Rcount == 0){
    P(WriteMutex);//只有第一个读者需要进行P()操作
}
++Rcount; //对于后面的读者只需计数加一即可
read;
--Rcount; //对于读者完成的情况,计数减一
if(Rcound == 0){
    V(WriteMutex); //最后一个读者离开,此时需要进行V()操作,这样写者才能进行写操作
}

同时,我们对读者计数Rcount也需要有互斥保护,因此读者操作改为:

P(CountMutex); //对读者计数Rcount互斥保护
    //在读者进来的时候进行计数判断
    if(Rcount == 0){
        P(WriteMutex);//只有第一个读者需要进行P()操作
    }
    ++Rcount; //对于后面的读者只需计数加一即可
V(CountMutex);
    read;
P(CountMutex);
    --Rcount; //对于读者完成的情况,计数减一
    if(Rcound == 0){
        V(WriteMutex); //最后一个读者离开,此时需要进行V()操作,这样写者才能进行写操作
    }
V(CountMutex);

可以看到,但等待队列里有读者的时候,只有最后一个读者离开的时候,写者才能开始执行写操作,因此,这是读者优先

写者优先

这里是使用的变量:

int readCount = 0, writeCount = 0; // 读者计数和写者计数
semaphore ReadCountMutex = 1,WriteCountMutex = 1; // 读者计数互斥和写者计数互斥
semaphore ReadMutex = 1, WriteMutex = 1; // 读互斥与写互斥

读者进程:

P(readMutex);  // 对读操作的临界区资源申请,这里需要注意和下面写者进程对比
    P(ReadCountMutex); // 对读者计数的互斥访问
        readCount++;
         if(readCount == 1){
            P(writeMutex); // 如果是第一个读者的话需要申请写互斥锁
         }
    V(ReadCountMutex);
V(readMutex);

read;

P(ReadCountMutex); // 对读者计数的互斥访问
    readCount--;
    if(readCount == 0){
        V(writeMutex);  // 如果是最后一个读者的话需要释放写互斥锁,这样的话已经在读的读者读完写者才能写
    }
V(ReadCountMutex);

写者进程:

P(WriteCountMutex);  // 对写者计数的互斥访问
    writeCount++;
    if(writeCount == 1){
        P(readMutex); // 如果是第一个写者,申请读互斥锁,这样如果现在有读者在读,写者会等待读者读完再开始写,而后面都来的读者因为申请不到读互斥锁,所以会一直等待,等到所有写者写完才会开始读操作,这样就实现了写者优先
    }
V(WriteCountMutex);

P(WriteMutex); // 写写互斥
    write;
V(WriteMutex);

P(WriteCountMutex); // 对写者计数的互斥访问
    writeCount--;
    if(writeCount == 0){
        V(readMutex); // 如果是最后一个写者,释放读互斥锁,此时之前到来等待的读者才能开始读
    }
V(WriteCountMutex);
  • 整个过程注释写的应该比较清楚了,这里就不赘述了,逃。。。

猜你喜欢

转载自blog.csdn.net/qiuxy23/article/details/81266732