互斥和同步-读者/写者问题
读者/写者问题
问题定义:
存在一个多进程共享的数据区,该数据区可以是 一个文件或者一块内存或者一组寄存器, 有些进程
reader
只读取该数据区中的数据, 有些进程writer
只往数据区写数据
满足条件
- 任意数量的读进程可以同时读该文件
- 一次只有一个写进程可以写文件
- 若一个写进程正在写文件,则禁止任何进程读文件
综上即: 读进程不需要排斥其他进程,而写进程需要排斥其他所有进程
一般互斥解决方案
- 因为读进程 和 写进程 都要访问 数据区,那么将 进程访问 数据区的部分声明为一个临界区,
缺点:
- 假设共享区是一个图书馆目录,普通用户访问目录查找书,管理员可以修改目录,
- 一般情况下,每次访问目录都相当于访问临界区,且用户每次又只能访问一个目录, 导致延迟过高,
读者优先 的解决方案
- 思路:
信号量
wsem
用于实施互斥(writesemaphore
), 当写进程正在访问共享数据区时,其他进程不能访问,同时读进程也通过
wsem
实施互斥,读者优先:
没有一个读进程在执行时, 第一个试图读的读进程在
wsem
上等待当已经存在至少一个读进程时, 随后的读进程无需等待,可直接进入
readcount
用于记录读进程的数量信号量
x
用于确保readcount
被正确更新
1 |
|
写者优先 的解决方案
读者优先的问题:
当一个读进程开始访问数据区时,只要至少有一个读进程正在读,那么为该进入的读进程 保留 数据区的控制权, 导致写进程可能处于饥饿状态
目的是解决 写进程可能处于饥饿的状态
思路
- 信号量
rsem
: 至少有一个写进程准备访问数据区时,用于禁止所有的读进程.* 0 : 有写进程,即 **写进程准备访问数据区** * 1 : 无准备访问数据区的 写进程,
- 变量
writecount
: 控制rsem
的设置 - 信号量
y
:类似x
: 控制writecount
的修改
- 信号量
1 |
|
rsem
的作用(个人理解)rsem
是为写进程而定义的, 在wsem
提供 读/写进程互斥的基础上 .又通过rsem
信号量提供: 当一个写进程准备进入数据区时, 保证新的读进程阻塞(不能进入数据区) 且写进程 等待已有的读进程退出数据区
信号量z的作用
- 由于可能有多个读进程都在等待
rsem
信号量,通过信号量z
使得等待rsem
信号量的队列改成在信号量z
上排队,那么每次只有一个只读进程在rsem
上排队
消息传递实现写进程的优先权 的解决方案
流程:
- 一个访问共享数据区的控制进程,其他访问该数据区的进程给控制进程发送消息请求,
- 若请求同意,会接收到控制进程发送的应答消息
OK
,并通过finished
消息表示访问完成. - 控制进程有3个信箱,接收对应的消息,
finished
,writerequest
,readrequest
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
52void reader(int i) {
message rmsg;
while (true) {
rmsg = i;
send(readrequest,rmsg); //发送地址:readrequest
receive(mbox[i],rmsg); //来源地址:mbox[i],
READUNIT();
rmsg = i;
send(finished,rmsg); //发送写完成的消息
}
}
void writer(int j) {
message rmsg;
while (true) {
rmsg = j;
send(writerequest,rmsg); // 第一次请求写的消息
receive(mbox[j],rmsg); //接收控制进程的同意消息
WRITEUNIT();
rmsg = j;
send(finished,rmsg); //发送 写操作 完成消息
}
}
void controller() {
while (true) {
if (count > 0) {
// 在处理 写 消息之前, 先处理所有的finished请求
if (!empty(finished)) {
receive();
count++;
}
else if (!empty(writerequest)) {
receive(writerequest,msg);
writer id = msg.id;
count = count - 100;
}
else if (!empty(readrequest)) {
receive(readrequest,msg);
count--;
send(msg.id,"OK");
}
}
if (count == 0) {
send(writer id,"OK");
receive(finished,msg);
count = 100;
}
wihle (count < 0) {
receive(finished,msg);
count++;
}
}
}流程分析
count
初始化为大于可能的读进程数的最大值.假设为100, 对读请求消息操作数为1,对写请求操作数为-100
count > 0
说明没有读进程在等待,且可能有读进程发出的finished
消息未处理,然后再处理写请求的消息,最后处理读请求的消息count == 0
,写进程发送给控制进程请求消息后收到OK
,并且又发送finished
给控制进程表示完成,然后控制进程恢复count = 100
count < 0
, 写进程发出一个请求消息,那么要消除所有读进程,提供receive(finished,msg)
.
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!