问题描述
- 共享数据的两类使用者:
- 读者:只读取数据,不修改
- 写者:读取和修改数据
- 需要满足的条件
- 读读允许:同一时刻,允许有多个读者同时读
- 读写互斥:没有写者时读者才能读,没有读者时写者才能写
- 写写互斥:没有其他写者时写者才能写
信号量描述
- 信号量 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);
- 整个过程注释写的应该比较清楚了,这里就不赘述了,逃。。。