生产者/消费者问题
这是一个著名的进程同步问题。描述为:
有一群生产者进程在生产产品,并将这些产品提供给消费者进程去消费。为使生产者进程与消费者进程能并发执行,在两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程将它所生产的产品放入一个缓冲区中;消费者进程可从一个缓冲区取走产品去消费。
所有的生产者进程和消费者都要互斥地访问公共缓冲区。即仓库一次只允许一个人进入。
不允许消费者进程到一个空缓冲去取产品,也不允许生产者进程向一个已装满且尚未被取走地缓冲区中投放产品。
生产者活动:
- 生产一个产品
- 申请空货架
- 能否进入仓库(一次只允许一个人在仓库)
- 送一个产品到仓库
- 处仓库,唤醒等待进入仓库的人
- 通知消费者可取货物数增加一个
消费者活动:
- 询问是否有货物
- 看能否进入仓库
- 取货物
- 出仓库
- 通知生产者空货架数增加了一个
生产者和消费者对缓冲区互斥访问是互斥关系,同时生产者和消费者又是一个相互协作的关系,只有生产者生产之后,消费者才能消费,他们也是同步关系。
对于生产者,如果缓存是满的就去睡觉。消费者从缓存中取走数据后就叫醒生产者,让它再次将缓存填满。若消费者发现缓存是空的,就去睡觉了。下一轮中生产者将数据写入后就叫醒消费者。
注意的是:只有别的进程才能叫醒自己,自己无法醒来。
不完善的解决方案会造成“死锁”,即两个进程都在“睡觉”等着对方来“唤醒”。
为解决这一类生产者-消费者问题,应该设置两个信号量,一个说明缓冲单元的个数,另一个说明满缓冲单元的数目。并且由于缓冲区是一个临界资源,所以还需设置一个互斥信号量。
伪代码实现:
int full=0;//满缓冲单元的数目
int empty=n;//空缓冲单元的数目
int mutex=1;//互斥信号量
Producer()
{
while(生产未完成)
{
生产一个产品;
P(empty);
p(mutex);
送一个产品至缓冲区;
V(mutex);
V(full);
}
}
Consumer()
{
while(继续消费)
{
P(full);
P(mutex);
从缓冲区取产品;
V(mutex);
V(empty);
消费一个产品;
}
}
1、若生产者进程已经将缓冲区放满,消费者进程并没有取产品,即 empty = 0,当下次仍然是生产者进程运行时,它先执行 P(mutex)封锁信号量,再执行 P(empty)时将被阻塞,希望消费者取出产品后将其唤醒。轮到消费者进程运行时,它先执行 P(mutex),然而由于生产者进程已经封锁 mutex 信号量,消费者进程也会被阻塞,这样一来生产者进程与消费者进程都
将阻塞,都指望对方唤醒自己,陷入了无休止的等待。
2、若消费者进程已经将缓冲区取空,即 full = 0,下次如果还是消费者先运行,也会出现类似的死锁。
不过生产者释放信号量时,mutex、full 先释放哪一个无所谓,消费者先释放 mutex 还是 empty 都可以。