读者写者问题描述:
读者只会读取数据,不会修改数据,而写者即可以读也可以修改数据。
- 同一时刻,允许多个读者同时读
- 没有写者时读者才能读,没有读者时写者才能写
- 没有其他写者时,写者才能写
1、读者优先
代码实现:
semaphore wMutex; //写操作的互斥信号量,初始值为1
semaphore rCountMutex; //对读者的互斥修改,初始值为1
int rCount = 0; //正在进行读操作的读者个数,初始化为0
void writer( )
{
while(TRUE){
P( wMutex); //进入临界区write( );
/*
*进行写操作
*/
V(wMutex);//进入临界区
}
}
void reader( )
{
while(TRUE){
P(rCountMutex);//进入临界区
if ( rCount == 0)
{
P(wMutex);//如果有写者,则阻塞写者
}
rCount++ //读者计数+1
V(rCountMutex );//离开临界区
/*
*进行读操作
*/
P(rCountMutex);//进入临界区
rCount--;//读完数据,准备离开
if ( rCount == 0)
{
V( wMutex);//最后一个读者离开了,则唤醒写者
}
V( rCountMutex ) ;//离开临界区
}
}
上面的这种实现,是读者优先的策略,因为只要有读者正在读的状态,后来的读者都可以直接进入,如果读者持续不断进入,则写者就无法得到运行。
2、写者优先
那既然有读者优先策略,自然也有写者优先策略:
- 只要有写者准备要写入,写者应尽快执行写操作,后来的读者就必须阻塞;
- 如果有写者持续不断写入,则读者就处于饥饿状态,一直无法进行读操作;
代码实现:
semaphore rCountMutex;//控制对Rcount的互斥修改,初始值为1
semaphore rMutex;//控制读者进入的互斥信号量者,初始值为 1
semaphore wCountMutex//控制 wCount互斥修改,初始值为1
semaphore wDataMutex//控制写者写操作的互斥信号量,初始值为1
int rCount = 0; //正在进行读操作的读者个数,初始化为0
int wCount = 0;//正在进行读操作的写者个数,初始化为0
void writer()
{
while(TRUE)
{
P(wCountMutex);//进入临界区
if ( wCount ==0 )
{
P( rMutex);1/当第一个写者进入,如果有读者则阻塞读者
}
wCount++; //写者计数+1
V(wCountMutex);//离开临界区
P( wDataMutex);//写者写操作之间互斥,进入临界区
/*
*进行写操作
*/
V(wDataMutex); //离开临界区
P(wCountMutex); //进入临界区
wCount--; //写完数据,准备离开
if (wCount == 0 )
{
V(rMutex);// 最后一个写者离开了,则唤醒读者
}
V(wCountMutex);//离开临界区
}
}
void reader()
{
while(TRUE)
{
P(rMutex);
P(rCountMutex);//进入临界区
if ( rCount == 0)
{
P(wDataMutex);//当第一个读者进入,如果有写者则阻塞写者写操作
}
rCount++;
V(rCountMutex);//离开临界区
V(rMutex);
/*
*进行读操作
*/
P(rCountMutex);//进入临界区
rCount--;
if (rCount == 0)
{
V(wDataMutex);//当没有读者了,则唤醒阻塞中写者的写操作
}
v(rCountMutex);//离开临界区
}
}
注意,这里rMute的作用,开始有多个读者读数据,它们全部进入读者队列,此时来了一个写者,执行了 P(
rMute)
之后,后续的读者由于阻塞在 rMute 上,都不能再进入读者队列,而写者到来,则可以全部进入写者队列,因此保证了写者优先。
同时,第一个写者执行了 P(
rMute)
之后,也不能马上开始写,必须等到所有进入读者队列的读者都执行完读操作,通过V(wDataMutex)唤醒写者的写操作。
3、读者写者公平策略
要求:
- 优先级相同;
- 写者、读者互斥访问;
- 只能一个写者访问临界区;
- 可以有多个读者同时访问临界资源;
具体代码实现:
semaphore rCountMutex; //控制对Rcount的互斥修改,初始值为1
semaphore wDataMutex //控制写者写操作的互斥信号量,初始值为1
semaphore flag; //用于实现公平竞争,初始值为1
int rCount = 0; //正在进行读操作的读者个数,初始化为0
void writer()
{
while(TRUE)
{
P( flag );
P(wDataMutex);//写者写操作之间互斥,进入临界区
/*
*进行写操作
*/
V(wDataMutex); //离开临界区
V(flag);
}
}
void reader( )
{
while(TRUE)
{
P(flag);
P(rCountMutex);//进入临界区
if (rCount==0)
{
P(wDataMutex);//当第一个读者进入,如果有写者则阻塞写者写操作
}
rCount++;
V(rCountMutex);//离开临界区
V(flag);
/*
*进行写操作
*/
P(rCountMutex ); //进入临界区
rCount--;
if (rCount == 0)
{
V(wDataMutex);//当没有读者了,则唤醒阻塞中写者的写操作
}
V(rCountMutex); //离开临界区
}
}
对比方案一的读者优先策略,可以发现,读者优先中只要后续有读者到达,读者就可以进入读者队列, 而写者必须等待,直到没有读者到达。
没有读者到达会导致读者队列为空,即 rCount==0,此时写者才可以进入临界区执行写操作。
而这里 flag 的作用就是阻止读者的这种特殊权限(特殊权限是只要读者到达,就可以进入读者队列)。
比如:开始来了一些读者读数据,它们全部进入读者队列,此时来了一个写者,执行 P(flag) 操作,使得后续到来的读者都阻塞在 flag 上,不能进入读者队列,这会使得读者队列逐渐为空,即 rCount 减为 0。
这个写者也不能立马开始写(因为此时读者队列不为空),会阻塞在信号量 wDataMutex 上,读者队列中的读者全部读取结束后,最后一个读者进程执行 V(
wDataMutex)
,唤醒刚才的写者,写者则继续开始进行写操作。
而如果是写者先到,然后读者随之而来,那么后面再来的写者程序也会应为读者的P(flag)阻塞再这个信号量之上,带读者读完后再能继续写。
从这两个方面来看确实完成了读者和写者的公平对待。