基于阻塞队列的生产者-消费者模型实现详解
创作时间:
作者:
@小白创作中心
基于阻塞队列的生产者-消费者模型实现详解
引用
CSDN
1.
https://blog.csdn.net/2301_79465388/article/details/146163027
阻塞队列(Blocking Queue)是实现生产者-消费者模型的重要数据结构,它允许生产者和消费者并发地进行数据生产和消费操作。本文将详细介绍基于阻塞队列的生产者-消费者模型的实现,包括模型的特点、代码实现以及多线程环境下的性能优化。
生产者消费者模型特点
生产者-消费者模型是一种经典的并发设计模式,其主要特点如下:
- 一个交易场所:以特定数据结构形式存在的一段内存空间
- 两种角色:生产者(生产线程)和消费者(消费线程)
- 三种关系:
- 生产者之间
- 消费者之间
- 生产者和消费者之间的关系
与阻塞队列结合后形成的特点:
- 同步生产和消费操作:在多线程环境下,生产者和消费者可以同时操作队列,但需要保证线程安全。
- 避免资源浪费:当队列满时,生产者需要等待;当队列空时,消费者需要等待,以此避免资源浪费和无效操作。
- 提高效率:通过设置高水位线和低水位线,适时唤醒多个生产者或消费者,提高生产和消费的效率。
阻塞队列的实现
下面是一个基于C++的阻塞队列实现:
#ifndef __BLOCK_QUEUE__
#define __BLOCK_QUEUE__
#include <cstdlib>
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <queue>
using std::cout;
using std::endl;
template <class T>
class BlockQueue
{
private:
bool Isfull()
{
return _queue.size() == _maxcap;
}
bool Isempty()
{
return _queue.empty();
}
public:
BlockQueue(int maxcap = 10)
: _maxcap(maxcap), _queue(), _l_water(0), _h_water(maxcap), _call_num(maxcap / 2)
{
if (maxcap <= 4)
{
_l_water = 0x3f3f3f3f;
_h_water = -0x3f3f3f3f;
_call_num = 1;
}
else
{
_l_water = maxcap / 4;
_h_water = maxcap * 3 / 4;
}
pthread_mutex_init(&_mutex, nullptr);
pthread_cond_init(&_p_cond, nullptr);
pthread_cond_init(&_c_cond, nullptr);
}
void Push(T &in)
{
pthread_mutex_lock(&_mutex);
while (Isfull())
{
pthread_cond_wait(&_p_cond, &_mutex);
}
_queue.push(in);
pthread_cond_signal(&_c_cond);
if (_queue.size() >= _h_water)
{
cout << "call lots comsumer" << endl;
int t = _call_num;
while (t--)
pthread_cond_signal(&_c_cond);
}
pthread_mutex_unlock(&_mutex);
}
void Pop(T *out)
{
pthread_mutex_lock(&_mutex);
while (Isempty())
{
pthread_cond_wait(&_c_cond, &_mutex);
}
*out = _queue.front();
_queue.pop();
pthread_cond_signal(&_p_cond);
if (_queue.size() <= _l_water)
{
cout << "call lots creator" << endl;
int t = _call_num;
while (t--)
pthread_cond_signal(&_p_cond);
}
pthread_mutex_unlock(&_mutex);
}
~BlockQueue()
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_p_cond);
pthread_cond_destroy(&_c_cond);
}
size_t GetSize()
{
return _queue.size();
}
private:
std::queue<int> _queue;
int _maxcap;
pthread_mutex_t _mutex;
pthread_cond_t _p_cond;
pthread_cond_t _c_cond;
int _l_water;
int _h_water;
int _call_num;
};
#endif
成员变量
std::queue<int> _queue
:标准库中的队列,用于存储数据。int _maxcap
:队列的最大容量。pthread_mutex_t _mutex
:互斥锁,用于保证生产和消费操作的互斥,避免数据竞争。pthread_cond_t _p_cond
和pthread_cond_t _c_cond
:条件变量,分别用于生产者和消费者的等待和唤醒。int _l_water
和int _h_water
:低水位线和高水位线,用于优化生产和消费的效率。int _call_num
:一次性唤醒的生产者或消费者数量。
构造函数
BlockQueue(int maxcap = 10)
- 初始化队列的最大容量和相关参数。
- 根据最大容量设置高水位线和低水位线。
- 初始化互斥锁和条件变量。
Isfull
和 Isempty
bool Isfull()
:判断队列是否已满。bool Isempty()
:判断队列是否为空。
Push
函数
void Push(T &in)
- 生产者向队列中添加数据。
- 先获取互斥锁,进入临界区。
- 如果队列已满,生产者在
_p_cond
条件变量上等待。 - 向队列中添加数据后,唤醒在
_c_cond
条件变量上等待的消费者。 - 如果队列中的数据量达到或超过高水位线,唤醒多个消费者。
- 释放互斥锁,退出临界区。
Pop
函数
void Pop(T *out)
- 消费者从队列中取数据。
- 先获取互斥锁,进入临界区。
- 如果队列为空,消费者在
_c_cond
条件变量上等待。 - 从队列中取出数据后,唤醒在
_p_cond
条件变量上等待的生产者。 - 如果队列中的数据量低于或等于低水位线,唤醒多个生产者。
- 释放互斥锁,退出临界区。
析构函数
~BlockQueue()
GetSize
函数
size_t GetSize()
总结与多线程分析
这段代码通过互斥锁和条件变量实现了一个线程安全的阻塞队列,能够有效地处理生产者和消费者之间的同步问题。通过设置高水位线和低水位线,可以在数据量较多或较少时适时唤醒多个生产者或消费者,提高队列的使用效率。
对于多线程环境,这段代码可以支持多个生产者和多个消费者。虽然临界区一次只允许一个线程访问,但生产者之间和消费者之间可以并发执行,从而提高整体效率。
生产消费模型的优势
- 协调忙闲不均
- 效率高
- 实现生产者和消费者之间的解耦和
整个系统共用一把锁,意味着一次只能有一个线程访问临界区。多生产多消费相对于单生产单消费而言,高效体现在生产者之间和消费者之间的并发:一个生产者访问队列的时候,其他生产者也在生产数据;消费者访问队列的时候,其他消费者也在消耗数据。
本文原文来自CSDN
热门推荐
各种书体临习方法大全【珍藏版】
如何改善眉骨突出与眼窝凹陷问题:全面解决方案与技巧分享
历史小说:在虚实间演绎鲜活的历史
《东邪西毒》经典台词:王家卫电影里的23句人生哲理
取保候审的概念、条件及程序详解
欧冠战报:巴萨4-1本菲卡!晋级8强+1250万奖金,拉菲尼亚2球1助
拌面酱汁调料做法?拌面美味再升级!
餐后3小时血糖9.3mmol/L正常吗
一文读懂:粘胶、莫代尔、莱赛尔纤维的前世今生
高效阅读秘籍:一年看完1000本书
2024药学是医学里最差的专业吗?就业前景分析
打板子是什么刑法:解析中国历史与现代刑罚体系
网银转账转错了怎么办?可以追回转错的钱吗?
银行卡转错账了能追回吗?别慌!这5步操作你必须知道,95%成功率追回攻略
乙巳年庚辰月己巳日的干支格局与应用解析
建禄格男命的特点:从职业选择到性格特征
命理中“建禄格”的鉴定原则与命理特征
2024戛纳影展获奖影片盘点——电影成为非主流的传统艺术
SQL Server数据库脚本导出完全指南
如何导出SQLServer数据库文件
手术伤口可以用碘伏吗
深度学习优化算法详解:从Momentum到Adam
在 Outlook 中建立群组
康复科普|扁平足的危害及治疗
住房贷款利息夫妻双方都可以填吗?小产权房贷款又有哪些规定?
以兑现补款的定义是什么?这种兑现方式有哪些特点?
人与人之间的相处靠的是共性和吸引
如何快速搭建内容安全审核系统?
守望先锋卢西奥英雄技能全面解析
全麦面包的热量及营养成分