文章目录
- 1 问题描述
- 2 问题分析
- 3 代码实现
- 4 分析总结
1 问题描述
桌子上有一只盘子,每次只能向其中放入一个水果。爸爸专向盘子中放苹果,妈妈专向盘子中放橘子,儿子专等着吃盘子中的橘子,女儿专等着吃盘子中的苹果。只有盘子空时,爸爸或妈妈才可向盘子中放一个水果。仅当盘子中有自己需要的水果时,儿子或女儿可以从盘子中取出水果。
2 问题分析
- 关系分析。找出题目中描述的各个进程,分析它们之间的同步、互斥关系。
多生产者-多消费者问题关系分析如下:
互斥关系: 对缓冲区(盘子)的访问要互斥地进行
同步关系(一前一后):
- 父亲将苹果放入盘子后,女儿才能取苹果
- 母亲将橘子放入盘子后,儿子才能取橘子
- 只有盘子为空时,父亲或母亲才能放入水果
注意:“盘子为空”这个事件可以由儿子或女儿触发,事件发生后才允许父亲或母亲放水果
- 整理思路。根据各进程的操作流程确定P、V操作的大致顺序。
由以上的同步、互斥关系分析,将多生产者-多消费者问题描绘出图形:
- 设置信号量。设置需要的信号量,并根据题目条件确定信号量初值。
(互斥信号量初值一般为 1,同步信号量的初始值要看对应资源的初始值是多少)
本问题中信号量设置如下:
semaphore mutex = 1; //实现互斥访问盘子(缓冲区)semaphore apple = 0; //盘子中有几个苹果 semaphore orange = 0; //盘子中有几个橘子semaphore plate = 1; //盘子中还可以放多少个水果
- 模型实现:
dad
:先准备一个苹果,放苹果之前,先判断盘子里是否为空(P一下盘子,检查盘子中还可以放多少个水果),然后再将苹果放入进去(V一下苹果,数量+1)
mom
:先准备一个橘子,放橘子之前,先判断盘子里是否为空(P一下盘子,检查盘子中还可以放多少个水果),然后再将橘子放入进去(V一下橘子,数量+1)
daughter
:拿苹果之前,先判断盘子里有没有苹果(P一下苹果,若没有苹果,自己被阻塞),然后告诉父母,盘子为空了(V一下盘子)
son
:拿橘子之前,先判断盘子里有没有橘子(P一下橘子,若没有橘子,自己被阻塞),然后告诉父母,盘子为空了(V一下盘子)
以下实现了所有进程之间的同步关系:
还需要实现各个进程对盘子(缓冲区)的互斥访问:
就是所有进程对盘子进行PV操作,即加锁和解锁的过程
3 代码实现
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(plate); //允许父母进程进程向盘子中取放水果吃苹果;}
}
son (){while(1){P(orange);P(mutex);从盘子中取出橘子;V(mutex);V(plate); //允许父母进程进程向盘子中取放水果吃橘子;}
}
4 分析总结
- 问题:可不可以不用互斥信号量?
结论:即使不设置专门的互斥变量mutex
,也不会出现多个进程同时访问盘子的现象
原因在于:本题中的缓冲区大小为1,在任何时刻,apple、orange、plate
三个同步信号量中最多只有一个是1。因此在任何时刻, 最多只有一个进程的P操作不会被阻塞,并顺利地进入临界区…
- 若将缓冲区设置为2会发生什么现象?
父亲P(plate),可以访问盘子→母亲P(plate),可以访问盘子→父亲在往盘子里放苹果,同时母亲也可以往盘子里放橘子。于是就出现了两个进程同时访问缓冲区的情况,有可能导致两个进程写入缓冲区的数据相互覆盖的情况。
因此,如果缓冲区大小大于1,就必须专门设置一个互斥信号量mutex
来保证互斥访问缓冲区。
- 总结:在生产者-消费者问题中,如果缓冲区大小为1,那么有可能不需要设置互斥信号量就可以实现 互斥访问缓冲区的功能。当然,这不是绝对的,要具体问题具体分析。
- 建议:在考试中如果来不及仔细分析,可以加上互斥信号量,保证各进程一定会互斥地访问缓冲区。 但需要注意的是,实现互斥的P操作一定要在实现同步的P操作之后,否则可能引起“死锁”。
理解理清复杂的同步关系:
在分析同步问题(一前一后问题)的时候不能从单个进程行为的角度来分析,要把“一前一后”发生的事看做是两种“事件”的前后关系。
比如,如果从单个进程行为的角度来考虑的话,我们会有以下结论:
- 如果盘子里装有苹果,那么一定要女儿取走苹果后父亲或母亲才能再放入水果
- 如果盘子里装有橘子,那么一定要儿子取走橘子后父亲或母亲才能再放入水果
- 这么看是否就意味着要设置四个同步信号量分别实现这四个“一前一后”的关系了?
正确的分析方法应该从“事件”的角度来考虑
我们可以把上述四对“进程行为的前后关系”抽象为 一对“事件的前后关系”
- 盘子变空事件→放入水果事件。“盘子变空事件”既可由儿子引发,也可由女儿引发;
- “放水果事件” 既可能是父亲执行,也可能是母亲执行。
- 这样的话,就可以用一个同步信号量解决问题了