传统艺能😎
小编是双非本科大二菜鸟不赘述,欢迎米娜桑来指点江山哦
1319365055
🎉🎉非科班转码社区诚邀您入驻🎉🎉
小伙伴们,满怀希望,所向披靡,打码一路向北
一个人的单打独斗不如一群人的砥砺前行
这是和梦想合伙人组建的社区,诚邀各位有志之士的加入!!
社区用户好文均加精(“标兵”文章字数2000+加精,“达人”文章字数1500+加精)
直达: 社区链接点我
概念😘
生产者消费者模型是指通过一个容器来解决生产者和消费者的强耦合问题,两者之间不直接通讯,而是通过容器来通讯,所以生产者生产完数据之后不用等待消费者处理,直接放到容器当中,消费者也不用找生产者要数据,而是直接从这个容器里取数据,这个容器就相当于一个缓冲区,平衡了生产者和消费者的处理能力,实际上就是用来解耦生产者和消费者的。
特点😍
生产者消费者模型是多线程同步与互斥的一个经典场景,其特点如下:
三种关系 \color{red} {三种关系} 三种关系: 生产者和生产者(互斥关系)、消费者和消费者(互斥关系)、生产者和消费者(互斥关系、同步关系)。
两种角色 \color{red} {两种角色} 两种角色: 生产者和消费者。(通常由进程或线程承担)
一个交易场所 \color{red} {一个交易场所} 一个交易场所: 通常指的是内存中的一段缓冲区。(可以自己通过某种方式组织起来)
我们用代码编写生产者消费者模型的时候,本质就是对这三个特点进行维护:
- 生产者和生产者的关系
- 消费者和消费者的关系
- 生产者和消费者的关系
生产者和消费者之间的容器可能会被多个执行流同时访问,因此需要将该临界资源用互斥锁保护起来。其中所有的生产者和消费者都会竞争式的申请锁,因此三种关系间都存在互斥关系, 那为什么生产者和消费者又存在同步关系? \color{red} {那为什么生产者和消费者又存在同步关系?} 那为什么生产者和消费者又存在同步关系?
生产者生产的数据将容器塞满后,数据生产就会生产失败,同理,容器当中的数据被消费完后,消费者就会消费失败。这样会引起另一方的饥饿问题,是非常低效的。我们应该让生产者和消费者访问该容器时具有一定的顺序性,就像管道通信一样。
互斥关系保证的是数据的正确性,而同步关系是为了让多线程之间协同起来
优点😁
解耦
支持并发
支持分配不均
如果我们在主函数中调用某一函数,那么我们必须等该函数体执行完后才执行后续代码,因此函数调用本质上是一种强耦合。对应到生产者消费者模型中,函数传参实际上就是生产者生产的过程,而执行函数体实际上就是消费者消费的过程,但消费者生产者可以同时进行活动,因此生产者消费者模型本质是一种松耦合。
基于阻塞队列的生产者消费者模型🤣
在多线程编程中,阻塞队列(Blocking Queue)是一种常用于实现生产者和消费者模型的数据结构。
与普通的队列的区别在于:当队列为空时,获取元素的操作将会被阻塞,直到队列中放入元素。
当队列满时,放入元素时会被阻塞,直到有元素从队列中取出。
模拟实现😂
为了方便理解,下面我们以单生产者、单消费者为例进行,其中的 BlockQueue 就是生产者消费者模型当中的交易场所,我们可以用 STL 库当中的 queue 进行实现。
#include <iostream>
#include <pthread.h>
#include <queue>
#include <unistd.h>
#define NUM 5
template<class T>
class BlockQueue
{
private:
bool IsFull()
{
return _q.size() == _cap;
}
bool IsEmpty()
{
return _q.empty();
}
public:
BlockQueue(int cap = NUM)
: _cap(cap)
{
pthread_mutex_init(&_mutex, nullptr);
pthread_cond_init(&_full, nullptr);
pthread_cond_init(&_empty, nullptr);
}
~BlockQueue()
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_full);
pthread_cond_destroy(&_empty);
}
//向阻塞队列插入数据(生产者调用)
void Push(const T& data)
{
pthread_mutex_lock