Readers Writers problem
读者-写者问题
问题描述:
有读者和写者两组并发进程,共享一个文件,当多个读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时,则可能导致数据不一致的错误。因此要求:
- 允许多个读者同时对文件执行读操作;
- 只允许一个写者对文件执行写操作;
- 任何写者在完成写操作前不允许其他读者或写者工作;
- 写者在执行写操作前,应让已有的写者和读者全部退出。
有三种解法:
-
读进程优先解法,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
semaphore rw = 1; semaphore mutex = 1; int count = 0; Reader() { while (1) { P(mutex); if (count == 0) { P(rw); } ++count; V(mutex); read(); P(mutex); --count; if (count == 0) { V(rw); } V(mutex); } } Writer() { while (1) { P(rw); write(); V(rw); } }
这种解法中读进程是优先的,当存在读者时,写者将被延迟,且只要有一个读者活跃,随后而来的读者都将被允许访问文件,从而导致写者长时间等待,并有可能出现写者饥饿现象。
-
写进程优先解法不会发生饥饿,简单概括就是在获取
rw
这个不公平的锁之前获取一个公平的锁w
,如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
semaphore w = 1; semaphore rw = 1; semaphore mutex = 1; int count = 0; Reader() { while (1) { P(w); P(mutex); if (count == 0) { P(rw); } ++count; V(mutex); V(w); read(); P(mutex); --count; if (count == 0) { V(rw); } V(mutex); } } Writer() { while (1) { P(w); P(rw); write(); V(rw); V(w); } }
这里的写进程优先是相对而言的,有些书上把这个算法称为 “读写公平法”,即读写进程具有一样的优先级。
-
“真正的写者” 优先会导致饥饿且比较复杂,考试一般不考,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
semaphore read = 1; // 代表临界资源的读取权 semaphore write = 1; // 代表临界资源的写入权 semaphore mutex_count_reader = 1; // 对变量 count_reader 的互斥锁 semaphore mutex_count_writer = 1; // 对变量 count_writer 的互斥锁 int count_reader = 0; // 用于记录正在读取的读者数量 int count_writer = 0; // 用于记录即将要写入的写者数量 Reader() { while (1) { P(read); // 每个读进程都需要获取临界资源的读取权 P(mutex_count_reader); if (count_reader == 0) { // 为了阻塞后续的写进程,第一个读进程要占有临界资源的写入权 P(write); } ++count_reader; V(mutex_count_reader); V(read); // 为了保证读进程同时访问,在这里把读取权让给后续的读进程 读数据 P(mutex_count_reader); --count_reader; if (count_reader == 0) { // 为了唤醒后续写进程,最后一个读进程要让出临界资源的写入权 V(write); } V(mutex_count_reader); } } Writer() { while (1) { P(mutex_count_writer); if (count_writer == 0) { // 为了阻塞后续的读进程,第一个写进程要占有临界资源的读取权 P(read); } ++count_writer; V(mutex_count_writer); P(write); // 获取临界资源的访问权 写数据 V(write); // 写入数据完成,让出临界资源的写入权 P(mutex_count_writer); --count_writer; if (count_writer == 0) { // 为了唤醒后续读进程,最后一个写进程要让出临界资源的写入权 V(read); } V(mutex_count_writer) } }
This post is licensed under
CC BY 4.0
by the author.