Linux生产者消费者模型

生产者消费者模型

什么是生产者消费者模型?

生产者消费者模式就是通过一个容器 来解决生产者和消费者的强耦合问题。

生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。
对于厂商,当厂商生成完商品后,并不会直接拿给消费者,而是将商品交付给超市,消费者去超市买商品;
对于消费者,不会直接去厂商买商品,而是去超市买;
这样生产者和消费者两个对象就解耦合了

在这里插入图片描述

生产者消费者模型的特点

321原则(便于记忆)

  1. 三种关系: 生产者和生产者(互斥)、消费者和消费者(互斥)、生产者和消费者(互斥、同步)
  2. 两种角色: 生产者和消费者(进程或线程)
  3. 一个交易场所: 通常指的是内存中的一段缓冲区
  • 产者和消费者之间的容器可能会被多个执行流同时访问,因此我们需要将该临界资源用互斥锁保护起来
  • 生产者消费者模型还支持多个线程一起进入,阻塞队列属于临界资源,所以同一时刻,不管是生产者还是消费者,都只允许一个线程进入到阻塞队列进行push或pop操作

生产者和生产者、消费者和消费者、生产者和消费者,它们之间为什么会存在互斥关系?

于生产者和消费者之间的容器可能会被多个执行流同时访问,因此我们需要将该临界资源用互斥锁保护起来。
其中,所有的生产者和消费者都会竞争式的申请锁,因此生产者和生产者、消费者和消费者、生产者和消费者之间都存在互斥关系。

生产者和消费者之间为什么会存在同步关系?

如果让生产者一直生产,那么当生产者生产的数据将容器塞满后,生产者再生产数据就会生产失败
反之,让消费者一直消费,那么当容器当中的数据被消费完后,消费者再进行消费就会消费失败

互斥关系保证的是数据的正确性,而同步关系是为了让多线程之间协同起来

生产者消费者模型优点

  • 解耦
  • 支持并发
  • 支持忙闲不均

如果我们在主函数中调用某一函数,那么我们必须等该函数体执行完后才继续执行主函数的后续代码,因此函数调用本质上是一种紧耦合

在生产者消费者模型中,函数传参实际上就是生产者生产的过程,而执行函数体实际上就是消费者消费的过程,但生产者只负责生产数据,消费者只负责消费数据,在消费者消费期间生产者可以同时进行生产,因此生产者消费者模型本质是一种松耦合

基于BlockingQueue的生产者消费者模型

在这里插入图片描述
与普通的队列的区别:

当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中放入了元素
当队列满时,往队列里存放元素的操作会被阻塞,直到有元素从队列中取出

#include <iostream>
#include <pthread.h>
#include <queue>
#include <unistd.h>

#define NUM 5

template<class T>
class BlockQueue
{
public:
	BlockQueue(int capacity= NUM)
		: _capacity(capacity)
	{
		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(&_mutex);
		while (IsFull())
		{
			//不能进行生产,直到阻塞队列可以容纳新的数据
			pthread_cond_wait(&_full, &_mutex);
		}
		_q.push(data);
		pthread_mutex_unlock(&_mutex);
		pthread_cond_signal(&_empty); //唤醒在empty条件变量下等待的消费者线程
	}
	// 从阻塞队列获取数据(消费者调用)
	void Pop(T& data)
	{
		pthread_mutex_lock(&_mutex);
		while (IsEmpty())
		{
			// 不能进行消费,直到阻塞队列有新的数据
			pthread_cond_wait(&_empty, &_mutex);
		}
		data = _q.front();
		_q.pop();
		pthread_mutex_unlock(&_mutex);
		pthread_cond_signal(&_full); // 唤醒在full条件变量下等待的生产者线程
	}
	bool IsFull()
	{
		return _q.size() == _capacity;
	}
	bool IsEmpty()
	{
		return _q.empty();
	}
private:
	std::queue<T> _q; // 阻塞队列
	int _capacity; // 阻塞队列最大容器数据个数
	pthread_mutex_t _mutex;
	// 通过条件变量来判断队列信息
	pthread_cond_t _full; // 表示阻塞队列是否为满的条件
	pthread_cond_t _empty; // 表示阻塞队列是否为空的条件
};
  • 当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中放入了元素;
  • 当队列满时,往队列里存放元素的操作会被阻塞,直到有元素从队列中取出;
  • 一个条件变量用来描述队列为空,另一个条件变量用来描述队列已满;
  • 阻塞队列是会被生产者和消费者同时访问的临界资源,因此我们需要用一把互斥锁将其保护起来;

判断是否满足生产消费条件时不能用if,而应该用while

pthread_cond_wait函数是让当前执行流进行等待的函数,是函数就意味着有可能调用失败,调用失败后该执行流就会继续往后执行
在多消费者的情况下,当生产者生产了一个数据后如果使用pthread_cond_broadcast函数唤醒消费者,就会一次性唤醒多个消费者,但待消费的数据只有一个,此时其他消费者就被伪唤醒了
为了确认是否真的满足生产消费条件,这里必须使用while进行判断

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值