1、读者-写者问题描述
\qquad
动机: 共享数据的访问,假设有两种类型的使用者:一种为读者,不需要修改数据;另一种为写者,其会读取和修改数据。问题的约束包括: 允许在同一时间有多个读者,但在任何时候都只有一个写者;当没有写者时,读者才能访问数据;当没有读者和写者时,写者才能访问数据;在任何时候只能有一个线程可以操作共享变量;读者优先,若在读者前面有写者在排队等待,读者可以越过写者优先进行排队。
\qquad
设计算法需要用到的共享数据: 包括数据集,信号量(CountMutex)初始化为1,信号量(WriteMutex)初始化为1,整数Rcount初始化为0(记录当前读者的个数)。
2、基于信号量实现
\qquad 基于读者优先和信号量的操作逻辑如下所示:
//基于信号量的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、基于管程实现
\qquad 基于写者优先和管程的操作逻辑如下所示:
//基于管程的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();
}