目录
生产者消费者问题
问题的提出
- 多个生产者、多个消费者通过共享含n个缓冲区的缓冲池Buffer协作;
- 其中生产者负责生产数据并投入缓冲池,消费者从缓冲池中取数据消费;
- 生产者和消费者,每次生产/消费1个数据,要求每个数据必须且只被消费一次;
- 缓冲池为临界资源。请用记录型信号量进行同步。
初步思考
1,生产者可以向未满的缓冲池中放入数据,消费者从非空的缓冲池中取出数据。
2,简化并忽略一些与同步无关的问题:
- 生产的数据以怎样的顺序放入Buffer?消费者怎样取出(进程何时/怎样被唤醒)?
- 假设数据按照次序依次放入缓冲区,不需要详细安排;
3,可以想到使用队列来实现数据资源描述,考虑现实中Buffer的实现,为了更充分的利用空间(当队列以数组形式实现时,防止出现假满队列的情况),采用循环队列。生产者生产一个数据,指针in前移,消费者消费一个数据,指针out前移;
进程资源共享关系和同步关系分析
1,思考:
- 几个进程?:两种进程(生产者,消费者);
- 共享什么样的资源?:生产者进程共享空缓冲区资源;消费者进程共享满缓冲区资源;
2,生产者进程一样,故可用一个模板来描述。消费者相同;
问题的具体解决
第一搏
考虑到有两种临界资源:空/满缓冲区资源,故设计两个资源信号量:full、empty,于是:
如图,可以实现:生产者进程共享空缓冲区资源,消费者共享满缓冲区资源,当资源不足时,可以对相应进程阻塞。
存在的问题
以入队为例,当执行完Buffer(in):=nextp时,时间片耗尽,这时又进来了一个生产者进程,空缓冲区资源足够,于是也到了Buffer(in):=nextp这一步,但由于上一个进程刚给in所指位置赋值,in还没来得及前移,in所指位置就又被新的数据覆盖,从而出现错误。
第二搏
问题存在的原因是:声明的变量in和out,此时也成为了临界资源,所以对入队和出队的操作也需要采取信号量机制,形成临界区;
设计互斥信号量:mutex(n.互斥量; 斥体; 互斥; 互斥锁; 互斥对象)
至此问题完美解决,上图就是生产者-消费者问题的标准解答!
多维度思考
1,单生产者、单消费者、多缓冲区
显然资源信号量:full和empty,不能省略(省去之后,无法标记缓冲区的状态);
若互斥信号量省去,会出现读的同时进行写,或者写的同时进行读;
因此无法简化。
2,多生产者、多消费者、单缓冲
此时资源信号量:full和empty初始取值为0,1;
设想运行过程如下:
- 开辟消费者进程,但是由于full为0,进入阻塞;
- 若仍是消费者进程,仍进入阻塞状态;
- 当开辟生产者进程时,empty变为0,在写入数据过程中又有一个生产者进程切换进来,但是此时empty为0,空缓冲资源不足,因此进入阻塞。也就是说此时第一个生产者进程执行完毕释放出full之前,任何生产者/消费者进程都将被阻塞;
- 第一个生产者进程释放full后,便唤醒下一个进程。若为生产者进程,则继续阻塞,否则执行消费者进程;
- 此后同理
由此可见,这种情况不需要设置互斥信号量mutex,也可实现进程互斥!
代码如下:
3,单生产者、单消费者、单缓冲
类似于2的简化版,但是程序仍然同2.
4,允许生产者写时,消费者可读
标准程序中,设置了一个mutex,使得任何时候只有一个生产者或是消费者进程执行;
为了使写时消费者可读,需要设置两个mutex:mutexP和mutexC,使得消费者与消费者进程、生产者与生产者进程之间互斥,而消费者进程与生产者进程可以同时进行。
5,缓冲池无限大
此时空缓冲区资源取之不尽,因此,在生产者在写入数据时不需要考虑申请空缓冲区资源wait(empty);
即可以省略共享信号量empty。
6,调整生产者wait顺序
如图:
当缓冲池放满数据(条件1),empty=0,full=n,又来一个生产者(条件2),在wait(empty)后进入阻塞,此时mutex已被阻塞,那么此后不论来生产者/消费者,都将阻塞,产生死锁!
7,调整消费者wait顺序
情况同6类似。
当缓冲池无数据(条件1),empty=n,full=0,又来一个消费者(条件2),在wait(full)后进入阻塞,此时mutex已被阻塞,那么此后不论来生产者/消费者,都将阻塞,产生死锁!
8,调整wait顺序(生产者和消费者)
会出现6,7中的问题。
9,调整signal顺序
当空缓冲资源empty=0时,此时一消费者进程执行完signal(empty),empty=1,若发生了进程切换(消费者行为已经结束),一生产者进程开始执行,但由于mutex未被上一进程释放,因此生产者虽然有空缓冲资源,却无法正常执行,违反了空闲让进的原则。