互斥和同步-生产者/消费者问题
信号量解决互斥
由于所有进程都需要访问共享资源,每个进程进入临界区前执行
semWait(s)
,若s
为负,则进程被阻塞,为正数则-1
,进程立即进入临界区,由于s
不为正,则其他任何进程不能进入临界区下面将 缓冲区作为 共享资源
生产者/消费者问题
问题:
有一个或多个生产者生产某种类型的数据,放置在缓冲区,,有一个消费者从缓冲区取数据,每次取一项,
操作系统为了保证避免对缓冲区的重复操作,即在任何时候只有一个主体(生产者或消费者)访问缓冲区
边界条件:
- 缓存区满时,生产者不会继续向其中添加数据,
- 缓存区为空时,消费者不会从中移走数据
二元信号量解决无限缓冲区的方法(直观但不正确)
缓冲区结构:
先假设缓冲区是一个无限长的线性元素数组
- 生产者生产后缓冲区索引(in)增加 1,消费者类似
- 为了避免从空的缓冲区读取数据,消费者必须确保已经生产(
in > out
)
生产者函数(producer
) 和 消费者函数(consumer
)
1 |
|
二元信号量实现
- N记录缓冲区数据项的个数
- 信号量S实施互斥
- 信号量delay用于迫使消费者在缓冲区为空时等待(semWait)
程序伪代码
- parbegin(p1,p2,p3,p4……):阻塞主程序,初始化并行过程p1,p2,p3,….,所有过程终止后主程序恢复执行
1 |
|
分析:
- 直观的看到
- 生产者任何时候都可以向缓冲区增加数据项,且通过
semWaitB(s) / semSignalB(s)
阻止消费者和其他生产者访问缓冲区- 生产者在临界区时(生产者在执行),若
n==1
执行semSignalB(delay)
通知消费者停止空时等待,消费者通过semWaitB(delay)
等待数据产生- 若 生产者总在消费者之前执行, 那么 消费者很少会被阻塞在信号量
delay
上
缺陷
- 当
n=0
时,第一个生产者(producer
)执行后.消费者consumer()
开始执行时。
- 观察
consumer()
函数,发现在判断缓冲区是否为空之前就执行了semSignalB(s)
,那么此时生产者立刻进入临界区,即生产者执行semWaitB(s)
开始生产,而此时消费者仍未判断缓冲区是否为空 - 生产者又生产一个数据
n++
后,此时n=1
且delay = 1
,而此时消费者才开始判断if(n == 0)
缓冲区大小但此时n
已经等于1
了,那么if(n==0) semWaitB(delay)
跳过, - 跳过后假设生产者未抢占信号量,那么消费者
while(true)
继续执行下去,又执行了一轮semWaitB(s) / semSignalB(s)
,此时n=0
,if()
判断后delay=0
,此时消费者继续执行(未被生产者抢占临界区),然后n=-1
,if(n==0)
判断失效,陷入死锁
总结
- 问题出在消费者的
semWaitB(delay)
提前了,导致if(n==0)
判断不在临界区中执行,并产生死锁
二元信号量中引入辅助变量解决死锁
原因
- 消费者的
semSignalB(s)
执行完后 -> 生产者抢占临界区执行后改变了全局变量n(即消费者未执行完就破破坏了它的数据),开一个临时变量保存消费者的n状态能解决死锁 - 消费者
semSignalB(s)
放在while()
最后 为什么不能解决死锁?- 若
n == 0
则semWaitB(delay)
,消费者被delay
阻塞 且 不能执行semSignalB(s)
释放信号量s - 也造成死锁
- 若
伪代码
1 |
|
一般信号量 解决方法
改进
- 变量
n
作为信号量,其值等于缓冲区中的项数
伪代码
1 |
|
分析
producer
中增加2个信号量n
s
以及consumer
中消耗2个信号量n
和s
即使
producer
执行完semSignal(s)
后 ->consumer
立即进入临界区,由于需要等待2个信号量,所以consumer
还是执行不了
假设semWait(s)
和semWait(n)
互换
- 如果缓冲区为0,生产者执行完
semSignal(s)
后还未semSignal(n)
,此时消费者立刻执行semWait(s)
进入临界区,那么生产者无法产生数据且消费之一直等待信号量n造成死锁
有限缓冲区
- 仍是线性元素数组且长度有限,通过对指针的大小取模来达到循环效果
生产者函数(producer
)和消费者函数consumer
表示形式
producer:
1
2
3
4
5
6
while (true) {
while ((in+1) % n == out) // 说明生产者指针要追上消费者了 即缓冲区满了,那么不能继续生产
/*啥都不做*/
b[in] = v;
in = (in + 1) % n;
}consumet:
1
2
3
4
5
6
7
while(true) {
while(in == out) //缓冲区为空,则等待,
/*啥都不做,因为不能取*/
w = b[out];
out = (out + 1) % n;
/*消费 w */;
}
信号量解决有限缓冲区生产者/消费者问题方法
n
:缓冲区数据项的个数,e
:空余空间的数量s
:互斥条件
1 |
|
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!