同步问题是一个复杂的问题,但是它也有自己的方法去处理、去分析。
PV操作系统的解题思路:
关系分析。找出题目中描述的各个进程,分析它们之间的同步、互斥关系。(从事件的角度分析)
整理思路。根据各进程的操作流程确定P、V操作的大致顺序。
设置信号量。设置需要的信号量,并根据题目条件确定信号量的初值。(互斥信号量初值一般为1,同步信号量的初始值要看对应资源的初始值是多少)
1.生产者-消费者问题
问题描述:系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓存区中取出一个产品并使用。(注:这里的"产品"理解为某种数据)
分析:
生产者、消费者共享一个初始为空、大小为n的缓冲区。
只有缓冲区没满时,生产者才能把产品放到缓冲区,否则必须等待。(缓冲区没满->生产者生产)
只有缓冲区不空时,消费者才能从中取出产品,否则必须等待。(缓冲区没空->消费者消费)
缓冲区是临界资源,各进程必须互斥的访问。
根据分析,两个同步,一个互斥,所以需要3个信号量。定义如下:
semaphore mutex=1;//互斥信号量,实现对缓冲区的互斥访问
semaphore empty=n;//同步信号量,表示空闲缓冲区的数量
semaphore full=0;//同步信号量,表示产品的数量,也即非空缓冲区的数量
//生产者
Producer(){
while(1){
生产一个产品;
P(mutex);
P(empty);
把产品放入缓冲区;
V(mutex);
V(full);
}
}
//消费者
Consumer(){
while(1){
P(mutex);
P(full);
从缓冲区中取走一个产品;
V(mutex);
V(empty);
使用产品;
}
}
注意:实现互斥的P操作一定要在实现同步的P操作之后。否则会造成死锁的现象。(V操作不会导致进程阻塞,因此两个V操作顺序可以交换)。
2.多生产者-多消费者问题
问题描述:桌子上有一个盘子,每次只能向其中放入一个水果。爸爸专向盘子中放苹果,妈妈专向盘子中放橘子,儿子装等着吃盘子中的橘子,女儿装等着吃盘子中的苹果。只有当盘子空时,爸爸或妈妈才能向盘子中放一个水果。仅当盘子中有自己需要的水果时,儿子或女儿可以从盘子中取出水果。
分析:
互斥关系:对缓冲区(盘子)的访问要互斥地进行
同步关系(一前一后):
父亲将苹果放入盘子后,女儿才能取苹果。
母亲将橘子放入盘子后,儿子才能取橘子。
只有当盘子为空时,父亲或母亲才能放入水果。
根据分析,三个同步,一个互斥,所以需要个信号量。定义如下:
semaphore mutex=1;//实现互斥访问盘子(缓冲区)
semaphore apple=0;//盘子中有几个苹果
semaphore orange=0;//盘子中有几个橘子
semaphore plate=1;//盘子中还可以放多少个水果
生产者、消费者定义如下:
Dad(){
while(1){
准备一个苹果;
P(plate);
P(mutex);
把苹果放入盘子;
V(mutex);
V(apple);
}
}
Mom(){
while(1){
准备一个橘子;
P(plate);
P(mutex);
把橘子放入盘子;
V(mutex);
V(orange);
}
Daughter(){
while(1){
P(apple);
P(mutex);
从盘子中取出苹果;
V(mutex);
V(palte);
吃掉苹果;
}
Son(){
while(1){
P(orange);
P(mutex);
从盘子中取出橘子;
V(mutex);
V(palte);
吃掉橘子;
}
注意:在本例子当中,如果不设置互斥信号量也是可以实现互斥的,原因是缓冲区大小是1,在任何时刻,三个同步信号量只有一个为1。
3.吸烟者问题
问题描述:假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它,但是要卷起并抽掉一支烟,抽烟者需要三种材料:烟草、纸、胶水。三个抽烟者中,第一个拥有烟草、第二个拥有纸、第三个拥有胶水。供应者进程无限地提供三种材料,供应者每次将两种材料放桌子上,拥有剩下那种材料的抽烟者卷一根烟并抽掉它,并给供应者进程一个信号告诉完成了,供应者就会放另外两种材料在桌上,这个过程一直重复(让三个抽烟者轮流地抽烟)
分析:
组合一:纸和胶水
组合二:烟草和胶水
组合三:烟草和纸
同步关系(从事件的角度分析):
桌子上有组合一->第一个抽烟者取走东西
桌子上有组合二->第二个抽烟者取走东西
桌子上有组合三->第三个抽烟者取走东西
发出完成信号->供应者将下一个组合放到桌上
和上题一样,本例子缓冲区大小为1,4个同步不信号量中同一时刻至多有一个值为1,所以不需要互斥信号量,定义如下:
semaphore offer1=0;//桌子上组合一的数量
semaphore offer2=0;//桌子上组合二的数量
semaphore offer3=0;//桌子上组合三的数量
semaphore finish=0;//抽烟是否完成
int i=0;//用于实现"三个抽烟者轮流抽烟"
供应者和抽烟者定义如下:
Provider(){
while(1){
if(i==0){
将组合一放桌上;
V(offer1);
}
else if(i==0){
将组合二放桌上;
V(offer2);
}
else if(i==0){
将组合三放桌上;
V(offer3);
}
i=(i+1)%3;
P(finish)
}
}
Smoker1(){
while(1){
P(offer1);
从桌上拿走组合一、卷烟、抽掉;
V(finish);
}
}
Smoker2(){
while(1){
P(offer2);
从桌上拿走组合二、卷烟、抽掉;
V(finish);
}
}
Smoker3(){
while(1){
P(offer3);
从桌上拿走组合三、卷烟、抽掉;
V(finish);
}
}