前言:
1.什么是生产者和消费者模型?
2.生产者与消费者模型遵循的规则
3.生产者与消费者需满足的条件
4.实现基于单链表的生产者与消费者模型
什么是生产者和消费者模型?
在实际的软件开发中,经常会遇到这样的场景:某个模块负责生产数据,这些数据由另一个模块来负责处理。产生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。
对于生产者/消费者模型来说,上面所述还需一个缓冲区,这个缓冲区处于生产者和消费者之间,作为一个中介。
生产者将数据放入缓冲区,而消费者从缓冲区取出数据。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICdzFWRoRXdvN1LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX9UFRORTSU9UNRpXTmZEWjZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TN1ADN1IzMwAzMwcDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
生产者和消费者模型为什么需要缓冲区?
1.解耦
假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(也就是耦合)。将来如果消费者的代码发生变化,可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降低了。
2.支持并发
生产者直接调用消费者的某个方法,还有另一个弊端。由于函数调用是同步的(或者叫阻塞的),在消费者的方法没有返回之前,生产者只好一直等在那边。万一消费者处理数据很慢,生产者就会白白糟蹋大好时光。
使用了生产者/消费者模式之后,生产者和消费者可以是两个独立的并发主体。生产者把制造出来的数据往缓冲区一丢,就可以再去生产下一个数据。基本上不用依赖消费者的处理速度。
3.支持忙闲不均
缓冲区还有另一个好处。如果制造数据的速度时快时慢,缓冲区的好处就体现出来了。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。等生产者的制造速度慢下来,消费者再慢慢处理掉。
生产者与消费者模型
遵循“321原则(只是为了好记忆而已,没有其他的特殊含义)”
三种关系:
生产者与生产者:互斥的关系
(例如生产同一商品的厂家之间的相互竞争)
消费者与消费者:互斥的关系
(消费者和消费者之间并没有同步的关系,例如,我们去超市买东西,不可能不允许其他人也去超市买东西,而且在资源充足的情况下,消费者和消费者之间并没有什么很明显的关系,只有在资源特别匮乏的时候,消费者与消费者之间才会形成竞争关系)。
生产者与消费者:同步与互斥的关系
(首先来说互斥关系,当生产者正在生产的时候,消费者不可能去拿数据,否则可能会导致数据的不一致问题,再来看看同步关系,生产者和消费者必须是同步的,否则会引发这样的问题,当生产者还没有生产的 时候,消费者一直占用锁资源,但是并没有资源可供消费者消费,但是由于消费者一直占用锁资源,可能会导致生产者的饥饿问题)。
两种角色:
生产者和消费者:在Linux下,一般是进程或者线程来充当这样的角色。
一个交易场所:(在使用单链表来实现的时候,这条单链表充当的就是这个交易场所)。
一般是内存中可以存储数据的一个缓冲区。(可以是结构体、数组、链表等数据结构)。
生产者向这个缓冲区中放数据,而消费者向这个交易场所拿数据。
生产者和消费者遵循下面的规则:
1.当生产者向这个缓冲区中放数据时,消费者想消费时,这时应该将消费者挂起等待。
2.如果消费者正从这个缓冲区中拿数据时,这时如果生产者还想向这个缓冲区放数据,那么就应该将生产者挂起等待。
上面两个规则本质上就是在遵循互斥原则。
当生产者生产满时就应该释放锁资源,使得消费者可以获得锁资源,从而可以从缓冲区中拿数据。
这个就是所谓的同步机制。
实现基于单链表的生产者和消费者模型:
结果如下:
每生产一次,消费者就消费一次。
与条件变量相关的函数:
1.条件变量的初始化:
条件变量:数据类型为pthread-cond_t
静态的条件变量可以使用宏来初始化,局部的条件变量需要使用函数pthread_cond_init来进行初始化。
2.唤醒线程:
pthread-cond_signal函数唤醒一个等待某个条件变量的线程,参数为条件变量(是一个输出型参数)。—》唤醒的这个线程可以直接获得锁。
pthread_cond-broadcast函数唤醒等待某个条件变量的所有线程。(但是唤醒所有线程可能会导致进程不稳定)。
3.线程等待资源:
从该函数可以看出:条件变量总是和一个Mutex相互配合使用。
一个线程可以调用pthread_cond_wait等待条件变量,该函数一般会做下面三个操作:
1.释放锁资源(Mutex)
2.阻塞等待(休眠状态)
3.当被唤醒时,获得锁资源并返回。(返回到之前等待时的下一条指令处,执行下面的操作)。
(这里获得的锁资源依然是之前等待时释放的锁资源)。