文章目录

  • 一、生产者消费者模型的基本原则
    • 💕💕生产者-消费者模型的 321 原则💕💕
  • 二、为何要使用生产者消费者模型
    • 1. 解耦
    • 2. 支持并发 (提高效率)
    • 3. 忙闲不均的支持
  • 三、基于 BlockingQueue 的生产者消费者模型
    • 1. 阻塞队列的特点
    • 2. C++ 模拟实现
    • 3. 封装更精细的版本
  • 五、总结


一、生产者消费者模型的基本原则

在多线程编程中,生产者消费者模型是一种常见的并发设计模式。它通过一个**缓冲区(阻塞队列)**来解耦生产者和消费者,从而解决两者之间的强耦合问题。生产者只需要负责将数据放入队列,而消费者只需要负责从队列中取数据。

这样设计的好处在于,生产者在完成数据生成后无需等待消费者处理,可以立即返回继续生成;而消费者也无需关心数据来自哪里,只需要从队列里取出任务并处理即可。这种方式既保证了系统的高效性,也增强了并发性。

可以把 321 原则用层次化结构来表达,比表格更直观:


💕💕生产者-消费者模型的 321 原则💕💕

3 个关系

  1. 生产者之间互斥:多个生产者不能同时写入队列,否则会破坏数据一致性。
  2. 消费者之间互斥:多个消费者不能同时读取同一数据,否则会出现重复消费。
  3. 生产者与消费者之间互斥与同步:队列为空时消费者等待,队列满时生产者等待,二者既互斥又必须协同。

2 个角色

  1. 生产者:负责不断产生数据并放入队列。
  2. 消费者:负责从队列中取出数据并进行处理。

1 个交易场所

阻塞队列 / 环形队列:作为共享缓冲区,承载生产与消费的衔接。


二、为何要使用生产者消费者模型

1. 解耦

生产者和消费者之间没有直接依赖关系,它们通过阻塞队列完成数据交互,降低了耦合度。


2. 支持并发 (提高效率)

生产者和消费者可以并发执行,充分利用 CPU 资源。这里提高效率并不是说生产者消费模型可以更快的派发任务,而是通过一个串行的交易场所,可以将任务派发给不同的线程,也可以由不同的线程同时生产,在生产者和消费者自己之间可以做到并发执行,因此提高了效率,毕竟将来派发任务只占有一点点的份额,执行任务才是大头。


3. 忙闲不均的支持

如果生产者生成速度快于消费者处理速度,多余的数据会存放在阻塞队列中;反之,消费者可以在数据不足时自动等待。这样有效平衡了生产和消费之间的速度差异。


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

1. 阻塞队列的特点

在普通队列中,如果取数据时队列为空会直接返回错误,而阻塞队列则会让取数据的线程阻塞等待直到有数据为止;同样地,如果存放数据时队列已满,线程也会被阻塞,直到有空余位置。这种机制天生适合生产者消费者模型。

在这里插入图片描述

在这里插入图片描述


2. C++ 模拟实现

BlockQueue.hpp

// 阻塞队列的实现
#pragma once#include <iostream>
#include <string>
#include <queue>
#include <pthread.h>const int defaultcap = 5; // for testtemplate <typename T>
class BlockQueue
{
private:bool IsFull() { return _q.size() >= _cap; }bool IsEmpty() { return _q.empty(); }public:BlockQueue(int cap = defaultcap) : _cap(cap), _csleep_num(0), _psleep_num(0){pthread_mutex_init(&_mutex, NULL);pthread_cond_init(&_full_cond, NULL);pthread_cond_init(&_empty_cond, NULL);}void Equeue(const T &in){pthread_mutex_lock(&_mutex);while (IsFull()){_psleep_num++;std::cout << "生产者,进入休眠了: _psleep_num" << _psleep_num << std::endl;pthread_cond_wait(&_full_cond, &_mutex);_psleep_num--;}// 走到这里一定有空间了_q.push(in);if (_csleep_num > 0){pthread_cond_signal(&_empty_cond);std::cout << "唤醒消费者..." << std::endl;}pthread_mutex_unlock(&_mutex);}T Pop(){// 消费者调用pthread_mutex_lock(&_mutex);while (IsEmpty()){_csleep_num++;pthread_cond_wait(&_empty_cond, &_mutex);_csleep_num--;}T data = _q.front();_q.pop();if (_psleep_num > 0){pthread_cond_signal(&_full_cond);std::cout << "唤醒消费者" << std::endl;}// pthread_cond_signal(&_full_cond);pthread_mutex_unlock(&_mutex);return data;}~BlockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_full_cond);pthread_cond_destroy(&_empty_cond);}private:std::queue<T> _q;int _cap;pthread_mutex_t _mutex;pthread_cond_t _full_cond;pthread_cond_t _empty_cond;int _csleep_num; // 消费者休眠的个数int _psleep_num; // 生产者休眠的个数
};

3. 封装更精细的版本

Cond.hpp

// 阻塞队列的实现
#pragma once#include <iostream>
#include <string>
#include <queue>
#include "Mutex.hpp"
#include "Cond.hpp"const int defaultcap = 10; // for testusing namespace MutexModule;
using namespace CondModule;template <typename T>
class BlockQueue
{
private:bool IsFull() { return _q.size() >= _cap; }bool IsEmpty() { return _q.empty(); }public:BlockQueue(int cap = defaultcap): _cap(cap), _csleep_num(0), _psleep_num(0){}void Equeue(const T &in){{LockGuard lockguard(_mutex);// 生产者调用while (IsFull()){// 应该让生产者线程进行等待// 重点1:pthread_cond_wait调用成功,挂起当前线程之前,要先自动释放锁!!// 重点2:当线程被唤醒的时候,默认就在临界区内唤醒!要从pthread_cond_wait// 成功返回,需要当前线程,重新申请_mutex锁!!!// 重点3:如果我被唤醒,但是申请锁失败了??我就会在锁上阻塞等待!!!_psleep_num++;std::cout << "生产者,进入休眠了: _psleep_num" << _psleep_num << std::endl;// 问题1: pthread_cond_wait是函数吗?有没有可能失败?pthread_cond_wait立即返回了// 问题2:pthread_cond_wait可能会因为,条件其实不满足,pthread_cond_wait 伪唤醒_full_cond.Wait(_mutex);_psleep_num--;}// 100%确定:队列有空间_q.push(in);// 临时方案// v2if (_csleep_num > 0){_empty_cond.Signal();std::cout << "唤醒消费者..." << std::endl;}}}T Pop(){T data;{// 消费者调用LockGuard lockguard(_mutex);while (IsEmpty()){_csleep_num++;_empty_cond.Wait(_mutex);_csleep_num--;}data = _q.front();_q.pop();if (_psleep_num > 0){_full_cond.Signal();std::cout << "唤醒生产者" << std::endl;}}return data;}~BlockQueue(){}private:std::queue<T> _q; // 临界资源!!!int _cap;         // 容量大小Mutex _mutex;Cond _full_cond;Cond _empty_cond;int _csleep_num; // 消费者休眠的个数int _psleep_num; // 生产者休眠的个数
};

五、总结

生产者消费者模型是一种经典的多线程设计模式。它利用阻塞队列实现生产与消费的解耦,不仅提高了系统的并发能力,还能平衡两者之间的处理速度差异。在 C++ 中,我们可以通过 queue + pthread + 条件变量 实现一个简易的阻塞队列,再结合任务封装,实现灵活的生产者消费者模型。

未来在实际工程中,如果使用 C++11 及以上版本,可以考虑用 std::mutexstd::condition_variable 替代 pthread,写法会更简洁。


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

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

相关文章

ensp启动路由器报错40

1. 先关闭 eNSP 模拟器、关闭 Virtualbox2. 在everything里面搜索 .VirtualBox文件夹&#xff0c;然后删掉3. 再打开 eNSP&#xff0c;不添加任何模拟设备&#xff0c;单击“菜单-工具-注册设备”&#xff0c;将 AR_Base 重新注册。4. 关闭 eNSP 模拟器

代码随想录二刷之“图论”~GO

A.深搜与广搜&#xff08;重点掌握&#xff01;&#xff01;&#xff01;&#xff01;&#xff09; 深搜类似于回溯法 搜索方向&#xff0c;是认准一个方向搜&#xff0c;直到碰壁之后再换方向换方向是撤销原路径&#xff0c;改为节点链接的下一个路径&#xff0c;回溯的过程…

基于Echarts+HTML5可视化数据大屏展示-白茶大数据溯源平台V2

效果展示&#xff1a;代码结构&#xff1a;主要代码实现 index.html布局 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta n…

Linux 系统网络配置及 IP 地址相关知识汇总

Linux 系统网络配置及 IP 地址相关知识汇总 一、IP地址基础 IP地址&#xff1a;在计算机网络中用来唯一标识一台设备的一组数字。 二、IPv4相关知识 1. IPv4的表示方法 采用点分十进制表示&#xff0c;即由4个0-255的十进制数通过点分隔组成&#xff08;如192.168.1.1&#xff…

百度股价突破120美元创年内新高,AI云成为增长新引擎

美东时间9月16日&#xff0c;百度&#xff08;NASDAQ: BIDU&#xff09;美股大涨近8%&#xff0c;收盘价突破120美元&#xff0c;站上124美元高位&#xff0c;创2023年10月以来新高。北京时间9月17日港股开盘&#xff0c;百度&#xff08;09888.HK&#xff09;港股再次暴涨&…

《彩虹六号:围攻》“Siege X”发布会3月14日举行!

使用jQuery的常用方法与返回值分析 jQuery是一个轻量级的JavaScript库&#xff0c;旨在简化HTML文档遍历和操作、事件处理以及动画效果的创建。本文将介绍一些常用的jQuery方法及其返回值&#xff0c;帮助开发者更好地理解和运用这一强大的库。 1. 选择器方法 jQuery提供了多种…

[从青铜到王者] Spring Boot+Redis+Kafka电商场景面试全解析

互联网大厂Java开发岗技术面试实录&#xff1a;严肃面试官VS搞笑程序员谢飞机 文章内容 第一轮&#xff1a;基础框架与并发控制&#xff08;电商系统基础能力&#xff09; 面试官&#xff08;严肃&#xff09;&#xff1a;欢迎进入面试环节&#xff0c;首先请用3句话总结Spring…

【DMA】DMA架构解析

目录 1 DMA架构 1. 芯片架构图一览 2. AHB总线矩阵挂载 3. AHB1/APB1的桥和AHB1/APB2的桥 4. DMA1 和 DMA2 的区别 2 AHB总线矩阵 1 DMA架构 1. 芯片架构图一览 2. AHB总线矩阵挂载 stm32F411 芯片的 AHB 总线矩阵上共挂载了 6 主 5 从 六主&#xff1a; Icode-bus、D…

GPS 定位器:精准追踪的“隐形守护者”

GPS 定位器&#xff1a;精准追踪的“隐形守护者” 一、什么是 GPS 定位器&#xff1f; GPS 定位器是一种基于 全球定位系统&#xff08;Global Positioning System, GPS&#xff09; 的智能追踪设备。 通过接收卫星信号并结合通信模块&#xff08;如 4G、NB-IoT&#xff09;&am…

前端拖拽排序实现

1. 使用 HTML5 事件 触发时机 核心任务 dragstart 开始拖拽时 准备数据&#xff0c;贴上标签 dragover 经过目标上方时 必须 preventDefault()&#xff0c;发出“允许放置”的信号 dragleave 离开目标上方时 清理高亮等临时视觉效果 drop 在目标上松手时 接收数据…

arm coresight

这是一个arm设计的调试基础架构&#xff0c;我们常用的debug基本都包含在内。比如ETM、PTM、ITM、HTM、ETB等。 注意ETM、PTM、ITM、HTM、ETB是coresight的子集。这些工具相比普通debug的断点调试&#xff0c;需要更高的专业水平&#xff0c;因此也用于复杂软件故障定位、性能…

《华为基本法》 —— 企业发展的导航仪

当一家企业从 “小作坊” 向 “规模化组织” 跨越时&#xff0c;最需要的是什么&#xff1f;华为的答案&#xff0c;藏在 1998 年出台的《华为基本法》里。1998 年&#xff0c;《华为基本法》正式颁布&#xff0c;这部凝结华为早期经营智慧的纲领性文件&#xff0c;不仅为华为从…

【完整源码+数据集+部署教程】传统韩文化元素分割系统: yolov8-seg-GFPN

背景意义 研究背景与意义 随着全球化的加速&#xff0c;传统文化的保护与传承面临着前所未有的挑战。尤其是韩国的传统文化&#xff0c;作为东亚文化的重要组成部分&#xff0c;蕴含着丰富的历史、艺术和哲学内涵。然而&#xff0c;随着现代化进程的推进&#xff0c;许多传统文…

构建AI智能体:三十五、决策树的核心机制(一):刨根问底鸢尾花分类中的参数推理计算

一、初识决策树想象一个生活中的场景&#xff0c;我们去水果店买一个西瓜&#xff0c;该怎么判断一个西瓜是不是又甜又好的呢&#xff1f;我们可能会问自己一系列问题&#xff1a;首先看看它的纹路清晰吗&#xff1f;如果“是”&#xff0c;那么它可能是个好瓜。如果“否“&…

c语言中实现线程同步的操作

线程 常见问题 同步权限 在多线程 / 多进程并发时&#xff0c;为避免共享资源&#xff08;如内存变量、硬件设备、文件&#xff09;被同时修改导致的数据不一致&#xff0c;需要通过 “同步机制” 控制谁能访问资源 ——“获取同步权限” 就是线程 / 进程申请这种访问资格的过程…

一台设备管理多个 GitHub 账号:从配置到切换的完整指南

一台设备管理多个 GitHub 账号&#xff1a;从配置到切换的完整指南 在日常开发中&#xff0c;我们经常需要在同一台电脑上使用多个 GitHub 账号&#xff08;比如个人账号和工作账号&#xff09;。但默认情况下&#xff0c;Git 会优先使用全局配置的账号&#xff0c;导致推送代…

即插即用,秒入虚拟:TouchDIVER Pro 触觉手套 赋能 AR/VR 高效交互

一、即插即用&#xff0c;零门槛开启沉浸之旅 在XR&#xff08;扩展现实&#xff09;技术高速发展的今天&#xff0c;用户对“真实感”的追求愈发迫切。Weart公司旗下旗舰产品TouchDIVER Pro触觉手套&#xff0c;凭借无需适配器、无需复杂设置的极简设计&#xff0c;打破传统触…

GitHub热榜项目 - 日榜之应用场景与未来发展趋势

一、引言GitHub热榜项目 - 日榜呈现出丰富多样的技术成果&#xff0c;这些项目蕴含着巨大的应用潜力&#xff0c;并且对未来数智化技术的发展有着重要的指示作用。深入探究其应用场景以及未来发展趋势&#xff0c;能让我们更好地把握技术发展方向&#xff0c;将这些前沿技术应用…

Linux网络:socket编程TCP

文章目录前言一&#xff0c;服务器端流程1-1 绑定协议1-2 绑定IP和端口1-3 监听客户端1-4 接收连接1-5 收发数据1-6 关闭连接1-7 服务端整体代码二&#xff0c;客户端流程2-1 指定地址和端口2-2 连接服务器2-3 发送消息2-4 客户端整体代码前言 TCP 的通信过程就像两个人打电话…

飞书智能查询机器人搭建说明文档

飞书智能查询机器人搭建说明文档 一、使用手册 1. 创建飞书机器人应用 如果仅需对接已有机器人应用则可跳过该步骤&#xff08;建议各业务部门独立使用各自的机器人应用&#xff09;。在飞书开发者后台中创建企业自建应用&#xff0c;添加机器人应用能力并申请对应的身份权限…