[多线程]基于阻塞队列(Blocking Queue)的生产消费者模型的实现

标题:[多线程]基于阻塞队列(Blocking Queue)的生产消费者模型的实现
@水墨不写bug

在这里插入图片描述


文章目录

  • 一、生产者消费者模型特点:
  • 二、实现
  • 2.1详细解释
    • 1. 成员变量
    • 2. 构造函数
    • 3. `Isfull` 和 `Isempty`
    • 4. `Push` 函数
    • 5. `Pop` 函数
    • 6. 析构函数
    • 7. `GetSize` 函数
  • 三、总结与多线程分析
  • 四、生产消费模型的优势与分析


一下的代码实现了一个阻塞队列(BlockQueue),用于生产者-消费者模型的实践。该模型允许生产者和消费者并发地进行数据生产和消费操作。

一、生产者消费者模型特点:

1.一个交易场所(特定数据结构形式存在的一段内存空间)
2.两种角色(生产者、消费者—生产线程、消费线程)
3.三种关系(生产者之间、消费者之间、生产者和消费者之间的关系)

与阻塞队列结合后形成的特点:

  1. 同步生产和消费操作:在多线程环境下,生产者和消费者可以同时操作队列,但需要保证线程安全。
  2. 避免资源浪费:当队列满时,生产者需要等待;当队列空时,消费者需要等待,以此避免资源浪费和无效操作。
  3. 提高效率:通过设置高水位线和低水位线,适时唤醒多个生产者或消费者,提高生产和消费的效率。

二、实现

#ifndef __BLOCK_QUEUE__
#define __BLOCK_QUEUE__#include <cstdlib>
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <queue>using std::cout;
using std::endl;// 阻塞队列的实现
// 生产消费模型的实践
// 生产和消费并发进行,3:三种关系(
//       当前实现是单生产,单消费,于是不需要考虑生产者与生产者之间、消费者与消费者之间的关系
//       仅仅考虑单个生产者和消费者之间的关系
// ——————当队列满,停止生产;队列为空,停止消费(线程同步)// ) 2:两个角色(consumer,creator) 1:一个交易场所(queue)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())// 队列满了,停止生产,在pcond条件变量下等待{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())// 队列空了,停止消费,在ccond条件变量下等待{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

2.1详细解释

1. 成员变量

  • std::queue<int> _queue:标准库中的队列,用于存储数据。
  • int _maxcap:队列的最大容量。
  • pthread_mutex_t _mutex:互斥锁,用于保证生产和消费操作的互斥,避免数据竞争。
  • pthread_cond_t _p_condpthread_cond_t _c_cond:条件变量,分别用于生产者和消费者的等待和唤醒。
  • int _l_waterint _h_water:低水位线和高水位线,用于优化生产和消费的效率。当队列中的数据量低于低水位线时,唤醒生产者;当数据量高于高水位线时,唤醒消费者。
  • int _call_num:一次性唤醒的生产者或消费者数量。

2. 构造函数

BlockQueue(int maxcap = 10)
  • 初始化队列的最大容量和相关参数。
  • 根据最大容量设置高水位线和低水位线,确保在数据量较多或较少时唤醒适当数量的生产者或消费者。
  • 初始化互斥锁和条件变量。

3. IsfullIsempty

  • bool Isfull():判断队列是否已满。通过比较队列当前大小和最大容量来实现。
  • bool Isempty():判断队列是否为空。通过检查队列是否为空来实现。

4. Push 函数

void Push(T &in)
  • 生产者向队列中添加数据。
  • 先获取互斥锁,进入临界区。
  • 如果队列已满,生产者在 _p_cond 条件变量上等待。
  • 向队列中添加数据后,唤醒在 _c_cond 条件变量上等待的消费者。
  • 如果队列中的数据量达到或超过高水位线,唤醒多个消费者。
  • 释放互斥锁,退出临界区。

5. Pop 函数

void Pop(T *out)
  • 消费者从队列中取数据。
  • 先获取互斥锁,进入临界区。
  • 如果队列为空,消费者在 _c_cond 条件变量上等待。
  • 从队列中取出数据后,唤醒在 _p_cond 条件变量上等待的生产者。
  • 如果队列中的数据量低于或等于低水位线,唤醒多个生产者。
  • 释放互斥锁,退出临界区。

6. 析构函数

~BlockQueue()
  • 销毁互斥锁和条件变量,释放资源。

7. GetSize 函数

size_t GetSize()
  • 返回队列的当前大小。

三、总结与多线程分析

总结:
这段代码通过互斥锁和条件变量实现了一个线程安全的阻塞队列,能够有效地处理生产者和消费者之间的同步问题。通过设置高水位线和低水位线,可以在数据量较多或较少时适时唤醒多个生产者或消费者,提高队列的使用效率。

多线程分析:
上面的代码实现是对应一个生产者一个消费者的情况。多生产多消费起始只需要创建多个生产者线程,多个消费者线程即可
因为临界区一次只允许一个线程访问,而这个线程是从哪来的?

对于生产者而言,生产者内部首先需要竞争出来一个生产者;
消费者也一样,需要竞争出来一个消费者;
然后,优胜的生产者和优胜的消费者之间还要进行一次竞争。
这一过程具体形象的解释了为什么这段代码可以不用改变就可以实现多生产多消费的情况。

四、生产消费模型的优势与分析

优势:

1.协调忙闲不均;
2.效率高;
3.实现生产者和消费者之间的解耦和;

整个系统共用一把锁,意味着一次只能有一个线程访问临界区。多生产多消费相对于单生产单消费而言,高效体现在哪里?
对于临界区的访问,多生产多消费和单生产单消费是没有区别的串行访问
但是产生任务,解决任务也需要耗费时间。高效体现在生产者之间和消费者之间的并发一个生产者访问队列的时候,其他生产者也在生产数据(构建请求)。消费者访问队列的时候,其他消费者也在消耗数据(解决请求)。


完~
转载请注明出处

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/33282.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在vs中无法用QtDesigner打开ui文件的解决方法

解决方法 右键ui文件&#xff0c;选择打开方式&#xff0c;弹出如下界面。 点击添加&#xff0c;弹出如下界面 点击程序后边的三个点&#xff0c;去电脑查找designer.exe,我的位置为D:\Qt\Qt5.9.9\5.9.9\msvc2015_64\bin\designer.exe。 名称可以自己起一个名字&#xff0c…

[内网渗透] 红日靶场2

环境配置 靶场地址: http://vulnstack.qiyuanxuetang.net/vuln/wiki/ 环境配置可以看这个: https://www.bilibili.com/video/BV1De4y1a7Ps/?spm_id_from333.337.search-card.all.click&vd_sourcecf73ac8de9b7c0322b1bccf77de91c5dNAT模式分配111段, DHCP也要更改 再添加…

第八节:红黑树(初阶)

【本节要点】 红黑树概念红黑树性质红黑树结点定义红黑树结构红黑树插入操作的分析 一、红黑树的概念与性质 1.1 红黑树的概念 红黑树 &#xff0c;是一种 二叉搜索树 &#xff0c;但 在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是 Red和 Black 。 通过对 任何…

基于Spring Boot的网上蛋糕售卖店管理系统的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

哈尔滨算力服务器托管推荐-青蛙云

哈尔滨年平均气温3.5摄氏度&#xff0c;有发展云计算和算力数据中心的天然优势 &#xff0c;今天为哈尔滨算力服务器托管服务商&#xff1a;青蛙云&#xff0c;黑龙江经营17年的老牌IDC服务商。 先来了解下算力服务器&#xff1a; 算力服务器&#xff0c;尤其是那些用于运行人…

关于Linux contOS 7 的防火墙

centos7 通过firewall-cmd命令添加防火墙白名单 。 查看防护墙状态 firewall-cmd --state 或 systemctl status firewalld active (running)-->表示防火墙已经开启&#xff1b;inactive (dead)-->表示防火墙已经关闭 开关防火墙命令 启动防火墙&#xff1a;systemctl …

【openGauss】物理备份恢复

文章目录 1. gs_backup&#xff08;1&#xff09;备份&#xff08;2&#xff09;恢复&#xff08;3&#xff09;手动恢复的办法 2. gs_basebackup&#xff08;1&#xff09;备份&#xff08;2&#xff09;恢复① 伪造数据目录丢失② 恢复 3. gs_probackup&#xff08;1&#xf…

MySql学习_基础Sql语句

目录 1.数据库相关概念 2.SQL 2.1 SQL通用语法 2.2 SQL分类 2.3 DDL&#xff08;数据库定义语言&#xff09; 2.4 DML&#xff08;数据操作语言&#xff09; 2.5 DQL&#xff08;数据查询语言&#xff09; 2.6 DCL&#xff08;数据控制语言&#xff09; 3. 函数 3.1 字…

MAE:Masked Autoencoders Are Scalable Vision Learners——论文学习

论文地址&#xff1a;https://arxiv.org/pdf/2111.06377.pdf 官方源码&#xff1a;https://github.com/facebookresearch/mae 一、主要内容 本文证明了掩码自编码器(MAE)是一种可扩展的计算机视觉自监督学习算法。本文的MAE方法很简单:屏蔽输入图像的随机补丁并重建缺失的像素…

【C++标准库类型】深入理解C++中的using声明:从基础到实践

目录 一、using声明基础 1.1 基本语法形式 1.2 典型应用场景 1.3 作用域规则 二、关键注意事项 2.1 命名冲突处理 2.2 头文件使用规范 2.3 与typedef的对比 三、面向对象中的应用 3.1. 解除派生类名称隐藏&#xff08;核心应用&#xff09; 3.2. 构造函数继承&#…

VSTO(C#)Excel开发6:与窗体交互

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

微服务Sentinel组件:服务保护详解

目录 服务保护简介 服务保护方案 安装与介绍Sentinel Sentinel整合微服务 服务保护实现 请求限流 线程隔离 OpenFeign整合Sentinel 配置线程隔离 服务熔断 编写降级逻辑 实现服务熔断 服务保护总结 服务保护简介 微服务保护是为了保障系统整体的稳定性和可靠性&am…

计算机视觉|首次写入政府工作报告!这个科技新词“具身智能”到底是什么?

一、具身智能与视觉-动作联合建模简介 具身智能&#xff08;Embodied Intelligence&#xff09; 是人工智能领域的关键研究方向&#xff0c;强调智能体通过物理实体与环境交互实现认知和智能行为。与传统人工智能基于静态数据和符号推理不同&#xff0c;具身智能依赖动态感知与…

【Azure 架构师学习笔记】- Azure Databricks (18) --Delta Live Table 架构

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Databricks】系列。 接上文 【Azure 架构师学习笔记】- Azure Databricks (17) --Delta Live Table和Delta Table Databrics DLT 是一个ETL 框架&#xff0c;通过创建pipeline来简化开发难度&#xff0c;本文介绍两种D…

上下文学习思维链COTPrompt工程

一、上下文学习 上下文学习强调在学习过程中考虑问题所处的上下文环境。 1.1 上下文学习的分类 零样本&#xff08;Zero-Shot&#xff09;上下文学习单样本&#xff08;One-Shot&#xff09;上下文学习少样本&#xff08;Few-Shot&#xff09;上下文学习 1.2 示例选择方法 …

嵌入式裸机设计--MCU常用裸机架构有哪些?

为什么是裸机设计 792125321入群学习更高效&#xff01; 在MCU&#xff08;微控制器单元&#xff09;裸机开发中&#xff0c;我们常见的架构设计主要围绕如何高效管理资源和任务调度。认识这些开发方式&#xff0c;对我们开发一个小型项目来说及有好处&#xff01; 下面介绍…

C语言基础知识04

指针 指针概念 指针保存地址&#xff0c;地址是字节的编号 指针类型和保存的地址类型要一直 使用时注意&#xff0c;把地址转换为&变量的格式来看 int a[3]; a转为&a[0] 指针的大小 64bit 固定8字节&#xff0c; 32bit 固定4字节 指针…

IDEA 一键完成:打包 + 推送 + 部署docker镜像

1、本方案要解决场景&#xff1f; 想直接通过本地 IDEA 将最新的代码部署到远程服务器上。 2、本方案适用于什么样的项目&#xff1f; 项目是一个 Spring Boot 的 Java 项目。项目用 maven 进行管理。项目的运行基于 docker 容器&#xff08;即项目将被打成 docker image&am…

浏览器崩溃的第一性原理:内存管理的艺术

作者&#xff1a;京东科技 屠永涛 登录后复制 你是否曾经遇到过浏览器突然卡顿&#xff0c;甚至崩溃的情况&#xff1f;尤其是在打开多个标签页或运行复杂的网页应用时&#xff0c;浏览器似乎变得异常脆弱。这种崩溃的背后&#xff0c;往往与内存管理息息相关。 1. 浏览器的内存…

Redis的缓存雪崩、缓存击穿、缓存穿透与缓存预热、缓存降级

一、缓存雪崩&#xff1a; 1、什么是缓存雪崩&#xff1a; 如果缓在某一个时刻出现大规模的key失效&#xff0c;那么就会导致大量的请求打在了数据库上面&#xff0c;导致数据库压力巨大&#xff0c;如果在高并发的情况下&#xff0c;可能瞬间就会导致数据库宕机。这时候如果…