视频链接:
https://www.bilibili.com/video/BV1YE411D7nH?p=25
一,问题描述:
有读者和写者两组并发进程,共享一个文件,当两个或两个以上的读进程同时访问共享数据时不会产生副作用,但如果某个写进程和写进程或读进程同时访问共享数据时可能会产生数据不一致的错误因此要求:
1)允许多个读者进程同时读取文件
2)只允许一个写者进程向文件中写数据
3)任何一个写者在完成写操作之前不允许其他读者或写者工作
4)写者进程执行写操作之前,应该让其他读进程或写进程全部退出
二,问题分析:
1,关系分析:
互斥关系:写进程——写进程,写进程——读进程
写者进程与任何进程都互斥,设置一个互斥信号量rw,在写者访问共享文件之前分别执行P,V操作。
读者进程和写者进程也要互斥因此读者访问共享文件前后也要对rw执行P,V操作。
但如果所有的读进程访问共享文件前后也要对rw执行P,V操作就会导致它们无法同时访问共享文件——读者-写者问题的核心思想就是解决这个问题。
2,解决办法:
我们把多个读进程看成一个整体,第一个读进程访问共享文件时,进行P操作对共享文件上锁,最后一个读进程完成后执行V操作对共享文件进行解锁,需要设置一个整形变量来记录当前有几个读进程在访问文件
三,具体实现:
semaphore rw=1; //用于实现对文件的互斥访问,表示当前是否有进程在共享文件
int count=0; //记录当前有几个读进程在访问文件
semaphore mutex=1;//用于保证对count变量的互斥访问
//写进程
writer(){
while(1){
P(rw);//写之前上锁
写文件;
V(rw);//写之后解锁
}
}
//读者进程
reader(){
while(1){
P(mutex);//用来保证对count的检查,和P(rw)的执行不被中断,防止两个读进程同时进行P(rw)而堵塞)
if(count==0)//如果count>0就不需要上锁,直接读文件
P(rw);//第一次访问的读进程负责上锁
count++;//访问的进程数加1
V(mutex);
读文件;
P(mutex);
count--;//读完文件后,对count减1
if(count==0)//如果当前读进程是最后一个
V(rw);//对文件进行解锁
V(mutex);
}
}
注意这样实现是有问题的:只要有源源不断的读进程到来,读进程就会一直执行,会导致写进程饿死
semaphore rw=1; //用于实现对文件的互斥访问,表示当前是否有进程在共享文件
int count=0; //记录当前有几个读进程在访问文件
semaphore mutex=1;//用于保证对count变量的互斥访问
semaphore w=1; //用来实现写进程优先
//写进程
writer(){
while(1){
P(w)
P(rw);//写之前上锁
写文件;
V(rw);//写之后解锁
V(w)
}
}
//读者进程
reader(){
while(1){
p(w);
P(mutex);
if(count==0)//如果count>0就不需要上锁,直接读文件
P(rw);//第一次访问的读进程负责上锁
count++;//访问的进程数加1
V(mutex);
v(w);
读文件;
P(mutex);
count--;//读完文件后,对count减1
if(count==0)//如果当前读进程是最后一个
V(rw);//对文件进行解锁
V(mutex);
}
}
加入P(w)和V(w)之后,很明显读写进程和写写进程的互斥访问可以保证,读读进程的同步也没问题,这时我们来实现读写进程的切换:
1,读进程先执行时,写进程是会堵塞在P操作处,很明显读进程执行时写进程无法进行,需要等到写进程完成后执行。
2,读进程开始执行,到读文件时,如果切换到写进程,写进程会通过P(w),堵塞在P(rw)无法继续执行,只能切换到写进程,如果此时有第二个读进程来读文件就会被堵塞在P(w),操作处,因为这被写进程上锁了,无法继续执行,只能由第一个读进程继续执行,并且当它执行了V(rw)操作时,写进程有了继续执行的条件,而读进程还是堵塞在P(w)处,只能切换成写进程执行。
这样就很好地解决了写进程饥饿的问题
四,回顾与思考:
这个问题的关键就是:设置了一个核心计时器count来记录当前正在访问的读进程数,我们可以用count值来判断是否第一个进程和最后一个进程来做不同的处理。
另外对count变量的检查和赋值不能一气呵成,导致的一些错误。需要实现一气呵成自然想到互斥信号量。最后我们还需要认真体会如何解决饥饿问题的