一、问题描述
信号量机制实现进程互斥的步骤:
- 设置初值为1的互斥信号量
- 在访问临界区之间进行P操作
- 在访问完临界区之后进行V操作
信号量机制实现进程同步的步骤:
- 设置初值为0的同步信号量
- 在前操作之后对同步信号量执行V操作
- 在后操作之前对同步信号量执行P操作
注意:
前操作指的就是需要先进行的操作,比如:当缓冲区已满的时候,消费者先取走缓冲区的产品,生产者才能生产产品放入缓冲区。这里的“消费者先取走缓冲区的产品”就是前操作;而” 生产者生产产品放入缓冲区“就是后操作
注意:
生产者-消费者问题中共存在3个信号量:一个是空闲缓冲区对应的信号量,一个是产品对应的信号量,还有一个是用来实现互斥访问临界区的互斥信号量
-
生产者每次要消耗一个空闲缓冲区,而消耗一个资源(空闲缓冲区)也就是对这个资源的信号量执行P操作。
因为P操作会对这个资源的信号量的数值进行-1操作,如果该信号量-1后的数值 < 0,说明在没有进行-1操作之前,信号量的数值 是<= 0的。也就是说,在没有进行-1操作之前已经没有资源(空闲缓冲区),资源(空闲缓冲区)的数量为0,所以会使用block原语使进程从运行态进入阻塞态,并把该进程挂到该信号量的等待队列(即阻塞队列)中。
直到消费者释放一个空闲缓冲区,也就是对空闲缓冲区对应的信号量执行了V操作之后,也就是增加一个空闲缓冲区之后,生产者进程才能再次被唤醒。 -
生产者进程在确认了空闲缓冲区的数量是足够的情况下,会生产一个产品(非空闲缓冲区),也就是对 产品所对应的信号量执行V操作。
因为V操作会对产品所对应的信号量的数值进行+1操作,相当于是往空闲缓冲区中放了一个产品,即非空闲缓冲区数量+1。如果进行+1操作之后,该信号量的数值<=0,说明在没有进行+1操作之前,该信号量的数值是<= -1的,这里的-1取绝对值之后代表的就是空闲缓冲区的数量,也就是等待队列中的进程数量为1。这时,会使用wakeup原语唤醒等待队列中的进程,让等待队列中的进程由阻塞态变为就绪态。
而消费者在消耗一个产品之前,需要对这个产品所对应的信号量执行P操作,表示要消耗一个产品(即非空缓冲区)。
二、问题分析
三、如何实现?
第一步:
首先,明确生产者需要做2间事情:生产一个产品 和 把产品放入缓冲区
消费者也需要做2间事情:从缓冲区取出一个产品 和 使用一个产品
第二步:
生产者生产一个产品之后,相当于是消耗了一个空闲的缓冲区,而空闲缓冲区的数量是由empty来控制的,需要将empty的数量进行-1操作,所以要在生产者生产一个产品之后执行P(empty)操作,表示消耗了一个空闲缓冲区。
第三步:
生产者把产品放入缓冲区之后,相当于是增加了一个产品(非空闲缓冲区),而非空闲缓冲区的数量是由full来控制的,需要将full的数量进行+1操作,所以要在生产者把产品放入缓冲区之后执行V(full)操作,表示增加了一个产品(非空闲缓冲区)
第四步:
消费者从缓冲区取出一个产品之前,需要执行P(full)操作,表示要消耗一个产品(非空缓冲区)
第五步:
消费者在使用产品之前,需要执行V(empty)操作,表示要增加一个空闲缓冲区
第六步:
实现多个生产者对生产者把产品放入缓冲区这个临界区的互斥访问
实现多个消费者对消费者从缓冲区取出产品这个临界区的互斥访问
生产者把产品放入缓冲区的操作应该放在临界区内,这样才能避免存在多个生产者并发的执行 将产品放入缓冲区的代码。所以要在进入临界区之前执行P(mutex)操作,离开临界区之后执行V(mutex)操作。
消费者同理,需要在进入临界区之前执行P(mutex)操作,离开临界区之后执行V(mutex)操作。
注意:
- 如果要实现互斥关系的话,这对P、V操作是在同一个进程中的
- 如果要实现(一前一后的)同步关系的话,这对P、V操作是分别位于两个不同的进程之中的
对“前V后P”的理解:
前操作之后执行V操作,后操作之前执行P操作
在缓冲区为空的情况下:生产者生产产品需要在消费者消费产品之前。因此,在生产者生产产品之后,需要执行V(full)操作;消费者消费产品之前,需要执行P(full)操作