操作系统——读者写者问题
读者写者问题
有读者和写者两组并发进程,共享一个文件,当两个或两个以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。
因此要求:
①允许多个读者可以同时对文件执行读操作;
②只允许一个写者往文件中写信息;
③任一写者在完成写操作之前不允许其他读者或写者工作;
④写者执行写操作前,应让已有的读者和写者全部退出。
-
关系分析。找出题目中各个进程之间的同步、互斥关系。
同步关系:个人感觉这个问题上并没有很强的同步关系。并不是说读完了,就一定要通知写进程写入数据。写完了,就一定要通知读进程读取数据。
互斥关系:读进程和写进程不能同时访问文件。写进程和写进程之间也存在互斥关系。但是读进程和读进程不存在互斥关系。
-
整理思路并设置信号量。设置需要的信号量,并根据题目中条件确定信号量初值。(互斥信号量初值一般为1)
文件在这里可以作为一个互斥资源(file,初始值为1),需要注意的是读进程和读进程之间并不存在互斥关系,所以我们需要想办法避免读进程和读进程之间去重复对文件加锁(这里加锁就指的是对信号量进行P操作)的过程。
写进程在操作之前先尝试能不能加锁成功,不能则阻塞;能则进行操作,操作完成后将资源进行释放。
读进程就稍稍麻烦一些,大概分为以下步骤:
- 首先需要维护一个当前正在读取文件的进程计数器。
- 在读进程读取文件之前先判断计数器是不是为0。
- 如果为0的话,就证明当前没有任何读进程在读取文件。所以此时需要尝试获取锁,如果不能获取到则证明有写进程在写文件,那么当前进程只能阻塞。如果能获取到锁,就可以读取文件,在真正I/O操作之前需要将计数器加1。
- 如果不是为0的话,就证明当前还有读进程在读取文件,此时读进程一定是占有锁的,那么当前进程就不用再去获取锁了,可以直接读取文件,同样在读文件之前需要将计数器加1。
- 在读取文件之后需要判断计数器是不是为1。
- 如果为不为1的话,则证明还有进程在读取文件,此时锁肯定是不能释放的。计数器需要减1。
- 如果为1,则证明当前没有进程在读取文件,此时需要释放锁,然后将计数器减1.
按照以上思路,可以简单的先实现一下代码,至于有什么其他问题,实现后再进行分析。
信号量
semapore file = 1;
int i = 0;//读者数量
写进程
void writer(){P(file);写入数据到文件;V(file);
}
读进程
void reader(){if(i == 0){P(file);}i++;从文件中读取数据;i--;if(i == 0){V(file);}}
}
仔细分析一下,读进程的代码存在并发问题,就是在if(i == 0)
的地方,如果有n个进程同时进入此判断,那么,必定会有n-1个进程会进入阻塞状态。所以各个读进程对计数器的判断也是属于互斥关系,那么就需要设置一个互斥信号量解决这个互斥问题。
信号量
semapore file = 1; // 解决文件互斥问题的信号量
int i = 0;//读者数量
semapore mutex = 1; // 解决读者数量互斥问题的信号量
写进程
void writer(){P(file);写入数据到文件;V(file);
}
读进程
void reader(){P(mutex);if(i == 0){P(file);}i++;V(mutex);从文件中读取数据;P(mutex);i--;if(i == 0){V(file);}V(mutex);}
}
这样一看,确实是解决了读进程的并发问题,但是如果一直有读进程在读数据的话,写进程就会一直被阻塞,直到饿死。所以还得再想一个方案。
仔细想想,出现读进程一直占用资源的问题无非就是,读写进程对信号量file的上锁的整个过程并不是互斥的,所以再增加一个锁对这个过程进行上锁就可以了。
信号量
semapore file = 1; // 解决文件互斥问题的信号量
int i = 0;//读者数量
semapore mutex = 1; // 解决读者数量互斥问题的信号量
semapore fair = 1;
写进程
void writer(){P(fair);P(file);V(fair);写入数据到文件;V(file);
}
读进程
void reader(){P(fair);P(mutex);if(i == 0){P(file);}i++;V(mutex);V(fair);从文件中读取数据;P(mutex);i--;if(i == 0){V(file);}V(mutex);}
}