问题描述
有一群生产者进程在生产产品,并将这些产品提供给消费者进程去消费.为使生产者进程与消费者进程能并发执行,在两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程将它所生产的产品放入一个缓冲区中;消费者进程可以从一个缓冲区中取走产品去消费。
问题的解?
代码
用一个指针in来指示下一个可利用的空缓冲区,用一个指针out来表示下一个可利用的满缓冲区。代码如下:
int in=0,out=0;
void producer() //生产者程序
{
while(true)
{
item=produceItem(); //生产一个产品
putItemIntoBuffer(item); //将产品放入缓冲区
in=(in+1)mod n; //修改指针位置
}
}
void consumer() //消费者程序
{
while(true)
{
item=removeItemFromBuffer(); //从缓冲区取走一个产品
out=(out+1)mod n; //修改指针位置
consumeItem(item); //消费该产品
}
}
分析
虽然上述的生产者和消费者程序在分别看时都是正确的,即使是顺序执行时结果也是对的,但是,当存在多个生产者,消费者时,就会出现错误,主要体现在以下方面:
1. 同步问题
即:不允许消费者进程到一个空缓冲区去取产品,也不允许生产者进程向一个满缓冲区投放产品。在上述程序中,这些情况均未加以控制。因此,可利用信号量empty和full分别表示缓冲区中空缓冲区和满缓冲区的资源数量。同时,假定只要缓冲区未满,生产者便可将产品送入缓冲区;只要缓冲区未空,消费者便可缓冲区中取走一个产品。
2.互斥问题
由于缓冲区是由生产者和消费者共享的存储空间,应当互斥访问,即:生产者修改缓冲区时,禁止消费者从缓冲区中取走数据;反之亦然。为此,可以利用互斥信号量mutex实现进程对缓冲区的互斥访问。
问题的解!
伪代码
至此,对生产者-消费者问题,伪码可修改如下:
int in=0,out=0;
semaphore empty,full,mutex;
empty.value=n; //空缓冲区资源信号量,初始状态有n个空的缓冲区
full.value=0; //满缓冲区资源信号量,初始状态有0个满缓冲区
mutex.value=0; //缓冲区互斥访问量
void producer() //生产者程序
{
while(true)
{
item=producerItem(); //生产一个产品
p(empty); //申请一个空缓冲区。如有,继续执行;否则,此处进程挂起
p(mutex); //如果没有其他进程访问缓冲区,继续执行;否则,进程挂起
putItemIntoBuffer(item); //将产品放入缓冲区
in=(in+1)mod n; //修改指针位置
v(mutex); //解锁,离开缓冲区,如果有其他进程等待进入,则唤醒
v(full); //增加一个满缓冲区,如果有消费者等待,则唤醒
}
}
void consumer() //消费者程序
{
while(true)
{
p(full); //如果有满缓冲区,则继续执行;否则,挂起
p(mutex); //如果没有其他进程访问缓冲区,继续执行;否则,挂起
item=removeItemFromBuffer(); //从缓冲区取走一个产品
out=(out+1)mod n; //修改指针位置
consume(item); //消费产品
v(mutex); //解锁,离开缓冲区,如果有其他进程等待进入,则唤醒
v(empty); //增加一个空缓冲区,如果有生产者等待,则唤醒
}
}
总结
- 首先,在每个进程中用于实现互斥的p(mutex)和v(mutex)必须成对出现。
- 其次,对资源信号量empty和full的p、v操作,同样需要成对出现,但他们分别处于不同的进程中。
- 最后,在每个程序中的多个p操作不能颠倒,应该先执行对资源信号量的p操作,然后再执行对互斥信号量的v操作,否则可能引起进程死锁。