生产者-消费者问题
问题描述
一组生产者进程和一组消费者进程共享一个初始为空、 大小为n的缓冲区,只有缓冲区没满时,生产者才能把消息放入缓冲区,否则必须等待:只有缓冲区不空时,消费者才能从中取出消息,否则必须等待。由于缓冲区是临界资源,它只允许一个生产者放入消息,或一个消费者从中取出消息。
分析
- 关系分析:可见生产者消费者之间存在互斥访问缓存区,且存在同步关系即生产者必须生产后消费者才能消费
- 整理思路:需要一个互斥信号量来互斥访问缓存区,同步关系存在缓存区满或空的情况,所以使用两个同步信号量来实现同步
- 信号量设置:mutex=1实现互斥,empty=N用来表示缓冲区为空,full=0表示缓冲区为空
伪代码
semaphore mutex = 1
semaphore empty = N
semaphore full = 0
producer(){
while(1)
生产产品
P(empty)
P(mutex)
放入缓存区
V(mutex)
V(full)
}
Customer(){
while(1)
P(full)
P(mutex)
取出产品
V(mutex)
V(empty)
消费
}
吃水果问题
问题描述
桌子上有一个盘子,每次只能向其中放入一个水果。爸爸专向盘子中放苹果,妈妈专向盘子中放橘子,儿子专等吃盘子中的橘子,女儿专等吃盘子中的苹果。只有盘子为空时,爸爸或妈妈才可向盘子中放一个水果:仅当盘子中有自己需要的水果时,儿子或女儿可以从盘子中取出。
分析
-
关系分析
可见爸爸和妈妈存在互斥关系,而爸爸与女儿、妈妈与儿子存在同步关系,可以将其看成变相的双生产者-双消费者问题
-
整理思路
使用plate实现互斥,而apple和orange实现同步
-
信号量设置
plate=1,apple=0,orange=0
伪代码
semaphore plate = 1
semaphore apple = 0
semaphore orange = 0
dad(){
while(1)
生成苹果
P(plate)
放入苹果
V(apple)
}
mom(){
while(1)
生成橘子
P(plate)
放入苹果
V(orange)
}
son(){
while(1)
P(orange)
取出橘子
V(plate)
吃
}
daughter(){
while(1)
P(apple)
取出苹果
V(plate)
吃
}
读者-写者问题
问题描述
有读者和写者两组并发进程,共享一个文件,当两个或以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。因此要求:①允许多个读者可以同时对文件执行读操作;②只允许一个写者往文件中写信息:③任一写者在完成写操作之前不允许其他读者或写者工作:④写者执行写操作前,应让已有的读者和写者全部退出
分析
-
关系分析
可见写者与写者之间、写者与读者之间存在互斥关系,如果此时为第一个读者我们则让写者休息,如果此时为最后一个读者我们则让写者开写,存在同步关系
-
整理思路
互斥我们使用rw实现,保证互斥;使用count来记录当前有多少个读者,使用mutex来互斥修改count
-
信号量设置
rw=1,mutex=1
伪代码1:读者优先
semaphore rw = 1
semaphore muetx = 1
int count = 0
writer(){
while(1)
P(rw)
写
V(rw)
}
reader(){
while(1)
P(mutex)
if (count == 0)
P(rw)
count++
V(mutex)
看书
P(mutex)
count--
if (count == 0)
V(rw)
V(mutex)
}
可见伪代码1存在一定问题,如果系统中有无数的读者进程,则写者将存在饥饿
伪代码2:写者优先或读写公平
使用w信号量,如果此时写者要写,则让写者先写
semaphore rw = 1
semaphore muetx = 1
semaphore w = 1
int count = 0
writer(){
while(1)
P(w)
P(rw)
写
V(rw)
V(w)
}
reader(){
while(1)
P(w) # 无写进程时访问
P(mutex)
if (count == 0)
P(rw)
count++
V(mutex) # 恢复共享文件
V(w)
看书
P(mutex)
count--
if (count == 0)
V(rw)
V(mutex)
}
哲学家进餐问题
问题描述
一张圆桌边上坐着 5名哲学家,每两名哲学家之间的桌上摆一根筷子,两根筷子中间是一碗米饭。哲学家们倾注毕生精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿时,才试图拿起左、右两根筷子(一根一根地拿起)。若筷子已在他人手上,则需要等待。饥饿的哲学家只有同时拿到了两根筷子才可以开始进餐,进餐完毕后,放下筷子继续思考。
分析
-
关系分析
可见哲学家对中间的筷子有互斥关系
-
整理思路
让哲学家拿起左右两个筷子即可,有两种解决方案:一次拿起两个筷子或对每个哲学家设置规则,防止死锁
-
信号量设置
chopsticks[5],即可
伪代码1:存在死锁
semaphore chopsticks[5] = [1, 1, 1, 1, 1]
Pi(){
while(1)
P(chopsticks[i]) # 拿左边的筷子
P(chopsticks[(i+1)%5]) # 拿右边的筷子
吃饭
V(chopsticks[(i+1)%5])
V(chopsticks[i])
}
可见存在死锁,即哲学家同时拿起左筷子;
解决方法:限制一次只能四个人拿筷子,或一次都拿完
伪代码2:一次只能最多四个人
semaphore chopsticks[5] = [1, 1, 1, 1, 1]
semaphore person = 4
Pi(){
while(1)
P(person) # 判断我能不能拿
P(chopsticks[i]) # 拿左边的筷子
P(chopsticks[(i+1)%5]) # 拿右边的筷子
吃饭
V(chopsticks[(i+1)%5])
V(chopsticks[i])
V(person)
}
伪代码3:一次性拿完
semaphore chopsticks[5] = [1, 1, 1, 1, 1]
semaphore mutex = 1
Pi(){
while(1)
P(mutex) # 判断我能不能拿
P(chopsticks[i]) # 拿左边的筷子
P(chopsticks[(i+1)%5]) # 拿右边的筷子
V(mutex)
吃饭
P(mutex)
V(chopsticks[(i+1)%5])
V(chopsticks[i])
V(mutex)
}
吸烟者问题
问题描述
假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它,但要卷起并抽掉一支烟,抽烟者需要有三种材料:烟草、纸和胶水。三个抽烟者中,第一个拥有烟草,第二个拥有纸,第三个拥有胶水。供应者进程无限地提供三种材料,供应者每次将两种材料放到桌子上,拥有剩下那种材料的抽烟者卷一根烟并抽掉它, 并给供应者一个信号告诉已完成,此时供应者就会将另外两种材料放到桌上,如此重复(让三个抽烟者轮流地抽烟)。
分析
-
关系分析
供应者与三个抽烟者有同步关系,且三个抽烟者轮流抽烟(互斥),抽完烟让供应者继续生产
-
整理思路
供应者为生产者,抽烟者为消费者
-
信号量设置
通过offer1,offer2,offer3来表示供应者三种组合,finish用来互斥抽烟
伪代码
int num = 0
semaphore offer1=0, offer2=0, offer3=0, finish=0
provider(){
while(1)
num++
num = num % 3
if num == 0
V(offer3) # 烟草和纸
if num == 1
V(offer1) # 胶水和纸
if num == 2
V(offer2) # 烟草和胶水
P(finish)
}
P1(){
P(offer1)
抽烟
V(finish)
}
P2(){
P(offer2)
抽烟
V(finish)
}
P3(){
P(offer3)
抽烟
V(finish)
}
总结
我还是不会同步问题,寄