本文讨论 m个生产者和 r个消费者怎样共享容量为 n的缓冲区。 n 不一定为1.
这问题与之前讨论的一个生产者和一个消费者共享一个容量为n的缓冲区问题不同, 在这个问题不仅生产者与消费者之间要同步, 而且m个生产者之间,r个消费者之间还必须互斥地访问缓冲区。 看一个例子:
桌子有一只盘子, 每次只能放入一只水果。 爸爸专向盘子里放苹果, 妈妈专向盘子里放橘子,一个儿子专吃盘子中的橘子,一个女儿专吃盘子中的苹果。 仅当盘子空闲时,爸爸或妈妈才可向盘子里存放一只水果。 仅当盘子中有自己需要的水果时,儿子或女儿才可从盘子里取出一只水果。 把爸爸,妈妈, 儿子, 女儿看做4个进程, 用 PV 操作进行管理,使这4个进程能正确地并发执行。
分析: 当爸爸向盘子中放了一只苹果后,应把“盘中有苹果”的消息发送给女儿; 当妈妈向盘子中放了一只橘子后,应把“盘中有橘子” 的消息发送给儿子。 儿子或女儿取走盘中的水果后, 应发送"盘子中又可存放水果"的消息。 这个消息不应特定地发送给爸爸或妈妈。 至于谁能向盘子中再放水果则应通过竞争共享资源(盘子)的使用权来决定。
怎样定义信号量呢? 首先应定义一个是否允许向盘子中存放水果的信号量mutex, 初值为1, 表示允许存放一只水果。 其次要定义两个信号量 SA 和 SO, 分别表示盘子中是否有苹果或橘子的消息, 初值为0. 儿子或女儿取走水果后, 可以通过调用V(mutex) 来达到发送"盘子中又可存放水果" 这一消息的目的, 而无需再增加信号量。
程序管理如下:
begin
mutex, SA, SO : semaphore;
mutex : =1; SA : =0; SO : =0;
cobegin
Process father
begin L1 : have an apple; /**准备了一只苹果*****/;
P(mutex); /**测试盘子中是否允许存放水果**/
put an apple ;
V(SA); /**发送盘中有苹果的消息**/
goto L1
end;
Process mother
begin L2 : have an orange; /**准备了一只橘子***/
P(mutex); /**测试能否使用运输工具 **/
put an orange;
V(SO); /**发送盘中有橘子的消息 **/
goto L2
end;
process son
begin L3 : P(SO); /**测试盘中是否有橘子*/
get an orange;
V(mutex); /**发送盘中又可存放水果的消息 */
eat an orange; /**吃橘子**/
goto L3
end;
process daughter
begin L4 : P(SA); /**测试盘中是否有苹果**/
get an apple;
V(mutex);
eat an apple;
goto L4
end;
coend;
end;
其中,爸爸或妈妈在向盘子里存放水果之前调用了 P(mutex)。 这个P(mutex)起两个作用:
1 由于mutex 的初值为1, 所以P(mutex) 限制了每次至多只有一个进程可以向盘子中放水果, 起到了互斥地向盘子中放水果的作用。
2 由于盘中有水果且尚未被取走时, mutex的值为0, 当盘中水果被取走后,调用了V(mutex) 使mutex的值又为1, 因此, P(mutex) 起到了测试 “盘中又可存放水果” 的消息是否到达的同步作用。
由此可见, 信号量 mutex 既被作为互斥的信号量, 又被作为同步的信号量, 起到了双重作用。