进程同步问题——生产者—消费者问题
问题梗概
生产者—消费者问题是相互合作关系的进程关系的一种抽象,是一个广义的概念,可以代表一类具有相同属性的进程
- 生产者:一个或者是多个生产者将生产的数据存入缓冲区中,通过in指针操作。
- 消费者:从缓冲数据中取出数据,通过out指针取出。
- 缓冲区:被消费者和生产者共享,循环使用,大小为n(存储单元的格数)
图示:
- 初始化缓冲区,in和out指针指向缓冲区的同一存储单元
- 写入数据,out指针后移,指针之前的单元格为满
- 取出数据,in指针后移,指针之前的单元格为空
访问特征
互斥访问缓冲区——单一访问
生产者和消费者必须一访问临界资源的互斥方式,访问缓存单元。在某一时间,仅仅允许一个单一的对象进缓存区访问。生产者放,消费者等待;消费者取,生产者等待;
同步访问缓冲区——配合访问
生产者和消费者必须相互沟通。即生产者不可以想满的数据里面写入数据,消费者不可以从空的缓冲区中取出数据。
- 生产者进入缓冲区,申请空的存储单元,没有则阻塞
- 消费者进入缓冲区,申请满的存储单元,没有则阻塞
访问方式总结
- 在互斥访问的基础上,外包一个同步访问。
- 互斥信号量,外套资源信号量。
- 唯一访问的方式,外包一个配合访问的方式。
问题模拟

生产者的阻塞与唤醒
- 生产一件商品,检查缓冲区的个数,如果已满,进入阻塞
- 否则将商品放入到缓冲区,每放入一件计数器加1,然后判断计数器的个数,决定是否需要唤醒消费者
- 如果等于1,说明在此之前缓冲区商品是0,消费者见到零为空就去阻塞,发出唤醒信号
- 不等于1,说明等待区中还有消费者
- 图例

消费者的阻塞与唤醒
- 先检查当前缓冲区的个数,如果为空,去阻塞
- 否则,进入缓冲区,取走一件商品,将商品计数器减一,然后判断计数器的个数,决定是否需要唤醒生产者
- 如果等于N - 1(N是缓冲区的容量),说明再去走之前是满的,即之前的生产者会陷入阻塞,需要叫醒
- 如果不等于,则无需叫醒生产者。
- 图例

总的流程模拟
情景说明,绿色的是资源所,对应绿色的符号为资源锁的钥匙,黄色的位互斥锁,唤醒两个进程中所有的在互斥锁前等待的对象,随机进入缓存区。
-
初始化,缓存区为空,所有钥匙都在生产者进程上,五个生产者,仅仅四把钥匙,一个在资源锁门口陷入阻塞。

-
然后生产者开始带着钥匙开始写入数据,out指针后移一位,满存储单元数加一,空存储单元数减一

-
生产者2推出缓存区,将将钥匙给所有互斥锁前面的阻塞进程(随机分的),消费者对应的互斥锁前面没有任何进程,最终只能是生产者3,进入缓存区,继续写入out指针后移。

-
生产者2将钥匙给消费者,消费者得到资源锁钥匙,来到互斥锁面前,并且陷入阻塞。

-
生产者3,将钥匙送到消费者待机区,生产者5随机进入缓存区,写入数据out指针后移,后将钥匙送到消费者对应的资源锁前面。

-
生产者4,又随机被唤醒,解开互斥锁,进入缓存区写入数据,out指针后移,将资源锁钥匙运到消费者资源锁前面。

-
在所有存储单元都满了的情况下,空存储单元数目为0,满存储单元数为4,消费者被唤醒,进入缓存区,拿走数据,in指针后移。

-
消费者和生产者双方都有了资源锁的钥匙,然后就开始随机进行选择,进入互斥锁的选择,为正常情况。


总结
- 资源锁的钥匙的数目就是缓存区存储单元的个数。
- 生产者少了一把钥匙,就意味着填满了一个存储单元,消费者就多了一把钥匙。
- 消费者少了一把钥匙,就意味着清空了一个数据单元,生产者就多了一把钥匙。
- 通过上述的模拟,不难看出仅仅只要对信号量机制的程序模板进行简单的修改就可以重复运用,不需要设置特殊的情况。
伪代码实现
- 采用信号量机制的模板,修改S.value的表示方法

总结
- 互斥信号量的P,V操作在每一个程序中必须成对出现
- 资源信号量(full,empty)也必须成对出现,但是可以处于不同的进程中
- 多个P的顺序不能够颠倒
- 上述问题是一个同步问题
- 消费者取商品,有界缓冲区至少有一个单元是满的
- 生产者放商品,有界缓冲区至少有一个是空的
- 有界缓冲区是临界资源,因此,生产者进程和消费者进程必须是互斥执行