读者-写者问题
问题描述:有读者和写者两组并发进程,共享一个文件,当两个或以上的读进程同时访问共享数据时不会产生副作用,但是如果某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。简单来说,你可以一个文档打开好多,但是你不能边读边写。从上述来看,得满足以下几个要求。
①允许多个读者可以同时对文件执行读操作;
②只允许一个写着往文件中写信息。
③任一写着再完成写操作前不允许或其他读者或写者工作;
④写者执行写操作前,应让已有的读者和写着全部退出。
问题分析:
- 关系分析。可以看出读者和写者是互斥,写者和写者也是互斥,读者和读者不存在互斥。
- 整理思路。两个进程,读者和写着。写者比较容易实现,它和任何进程互斥,用互斥信号量P和V可以解决。读者比较复杂,它必须在实现与写者互斥的同时,实现与其他读者同步,因此一对P和V是无法解决问题的。这里用到了一个计数器count,用它来判断是否由读者读文件。当有读者时,写者无法写文件,当没有读者时,写者才可以写文件。同时,这里不会读者对计数器的访问也应该是互斥的。
- 信号量设置。count计数器,用于记录当前读者的数量,初始值为0;设置mutex为互斥信号量,用于保护更新count变量时的互斥;设置互斥信号量rw,用于保证读者和写者的互斥访问。
代码描述如下:
- int count=0;//用于记录当前的读者数量
- semaphore mutex=1;//用于保护更新count变量时的互斥
- semaphore rw=1;//用于保证 读者和写者互斥地访问文件
- writer(){ //写者进程
- while(1){
- P(rw); //互斥访问共享文件
- writing;//写入
- V(rw);//释放共享文件
- }
- }
- reader(){//读者进程
- while(1){
- P(mutex);//互斥访问count变量
- if(count==0) //当第一个读进程读共享文件时
- P(rw); //阻止写进程
- count++; //读者+1
- V(mutex); //释放互斥变量count
- reading; //读取
- P(mutex); //互斥访问count变量
- count--; //读者减1
- if(count==0) //没有读者了
- V(rw); //允许写进程写
- V(mutex); 释放互斥变量count
- }
- }
从上述可以看出,读进程是优先的,只有出现了读进程,那么写操作就会被延迟,且只要有一个读进程活跃,那么它后面的都允许访问文件。这样会让写进程一直等待,会出现饿死的情况。如果要写进程优先的话,只要在读写进程上增加一对PV操作即可,代码描述如下,黑色加粗为增加的地方。
- int count=0;//用于记录当前的读者数量
- semaphore mutex=1;//用于保护更新count变量时的互斥
- semaphore rw=1;//用于保证 读者和写者互斥地访问文件
- semaphore w=1;//用于实现“写优先”
- writer(){ //写者进程
- while(1){
- P(w);//在无写进程请求时进入
- P(rw); //互斥访问共享文件
- writing;//写入
- V(rw);//释放共享文件
- V(w);//恢复对共享文件的访问
- }
- }
- reader(){//读者进程
- while(1){
- P(w);//在无写进程请求时进入
- P(mutex);//互斥访问count变量
- if(count==0) //当第一个读进程读共享文件时
- P(rw); //阻止写进程
- count++; //读者+1
- V(mutex); //释放互斥变量count
- V(w);//恢复对共享文件的访问
- reading; //读取
- P(mutex); //互斥访问count变量
- count--; //读者减1
- if(count==0) //没有读者了
- V(rw); //允许写进程写
- V(mutex); 释放互斥变量count
- }
- }
其实这里的写优先是相对的,并不是真正的写进程优先,有些书上,也把这个算法成为读写公平法,它只是不会让写进程“饿死”。