【Linux】生产者和消费者模型

      • 生产者和消费者概念
      • 基于BlockingQueue的生产者消费者模型
      • 全部代码

生产者和消费者概念

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

生产者和消费者彼此之间不直接通讯,而通过这个容器来通讯,所以生产者生产完数据之后不用等待消费者处理,直接将生产的数据放到这个容器当中,消费者也不用找生产者要数据,而是直接从这个容器里取数据,这个容器就相当于一个缓冲区,平衡了生产者和消费者的处理能力,这个容器实际上就是用来给生产者和消费者解耦的。

在这里插入图片描述
生产者消费者模型的特点

生产者消费者模型是多线程同步与互斥的一个经典场景,其特点如下:

  • 三种关系: 生产者和生产者(互斥关系)、消费者和消费者(互斥关系)、生产者和消费者(互斥关系、同步关系)。
  • 两种角色: 生产者和消费者。(通常由进程或线程承担)
  • 一个交易场所: 通常指的是内存中的一段缓冲区。(可以自己通过某种方式组织起来)

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

因为在生产者和消费者之间存在多种执行流同时访问的问题,,因此我们需要将他们同时访问的临界区进行加互斥保护起来
其中,所有的生产者和消费者都会竞争式的申请锁,因此生产者和生产者、消费者和消费者、生产者和消费者之间都存在互斥关系。

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

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

虽然这样不会造成任何数据不一致的问题,但是这样会引起另一方的饥饿问题,是非常低效的。我们应该让生产者和消费者访问该容器时具有一定的顺序性,比如让生产者先生产,然后再让消费者进行消费。

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

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

当多个生产者,消费者同时出现进行抢占线程时,我们可以使用BlockingQueue来进行缓冲,如图

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

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

知识联系: 看到以上阻塞队列的描述,我们很容易想到的就是管道,而阻塞队列最典型的应用场景实际上就是管道的实现。

在这里插入图片描述
put为生产者,take为消费者

全部代码

task.hpp 用于实现打印和计算

#pragma once
#include <iostream>
#include <string>class Task
{
public:Task(){}Task(int x,int y,char op):_x(x),_y(y),_op(op),_result(0),_exitCode(0){}void operator()(){switch(_op){case '+' :_result = _x + _y;break;case '-':_result = _x - _y;break;case '*':_result = _x * _y;break;case '/':{if (_y == 0)_exitCode = -1;else_result = _x / _y;}break;case '%':{if (_y == 0)_exitCode = -2;else_result = _x % _y;}break;default:break;}}std::string formatArg(){return std::to_string(_x) + _op + std::to_string(_y) + "="; }std::string formatRes(){return std::to_string(_result) + "(" + std::to_string(_exitCode) + ")";}~Task(){}
private:int _x;int _y;char _op;//输入的符号int _result;int _exitCode;
};

blockQueue.hpp 维护线程之间的同步

#pragma once 
#include <iostream>
#include <queue>
#include <pthread.h>const int gcap = 5;//最大容量
template<class T>
class BlockQueue
{
public:BlockQueue(const int cap = gcap):_cap(gcap){pthread_mutex_init(&_mutex,nullptr);//初始化互斥量//初始化用户和生产者的条件变量pthread_cond_init(&_consumerCond,nullptr);pthread_cond_init(&_consumerCond,nullptr);}//判断是否为慢bool isFull(){return _q.size() == _cap;}//判断是否为空bool isEmpty(){return _q.empty();}//插入void push(const T &in){pthread_mutex_lock(&_mutex);//细节1:一定要保证,在任何时候,都要符合条件,才进行生产while(isFull()){//1 我们只能在临界区内部,判断临界区资源是否就绪!注定了我们在当前一定持有锁。//2 要让线程进行休眠等待,不能持有锁等待//3 注定了pthread_cond_wait要有锁的释放能力pthread_cond_wait(&_productCond,&_mutex);// 4. 当线程醒来的时候,注定了继续从临界区内部继续运行!因为我是在临界区被切走的!// 5. 注定了当线程被唤醒的时候,继续在pthread_cond_wait函数出向后运行,又要重新申请锁,申请成功才会彻底返回}// 没有满,就要让他继续运行_q.push(in);//加策略pthread_cond_signal(&_consumerCond);pthread_mutex_unlock(&_mutex);}//取出删除void pop(T* out){pthread_mutex_lock(&_mutex);while(isEmpty()) {pthread_cond_wait(&_consumerCond, &_mutex);}*out = _q.front();_q.pop();// 加策略pthread_cond_signal(&_productCond);pthread_mutex_unlock(&_mutex);}~BlockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_consumerCond);pthread_cond_destroy(&_productCond);}
private:std::queue<T> _q;int _cap;//生产容量//为什么我们这份代码只有一个锁,根本原因在于//我们生产者和消费者访问的是同一个queue && queue 被当作整体使用pthread_mutex_t _mutex;pthread_cond_t _consumerCond;//消费者对应的条件变量pthread_cond_t _productCond;//生产者对应的条件变量
};

main.cc

#include <iostream>
#include "task.hpp"
#include "blockQueue.hpp"
#include <pthread.h>
#include <ctime>
#include <unistd.h>void* consumer(void* args)
{BlockQueue<Task>* bq = static_cast<BlockQueue<Task>*>(args);while(1){Task t;// 1. 将数据从blockqueue中获取 -- 获取到了数据bq->pop(&t);t();// 2. 结合某种业务逻辑,处理数据! -- TODOstd::cout << pthread_self() << " | consumer data: " << t.formatArg() << t.formatRes() << std::endl;}
}void *productor(void *args)
{BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);std::string opers = "+-*/%";while(1){int x = rand()%20 +1;int y = rand()%20 +1;char op = opers[rand() % opers.size()];Task t(x,y,op);bq->push(t);std::cout << pthread_self() << " | productor Task: " <<  t.formatArg() << "?" << std::endl;}
}int main()
{BlockQueue<Task>*bq = new BlockQueue<Task>();pthread_t c[2], p[3];pthread_create(&c[0], nullptr, consumer, bq);pthread_create(&c[1], nullptr, consumer, bq);pthread_create(&p[0], nullptr, productor, bq);pthread_create(&p[1], nullptr, productor, bq);pthread_create(&p[2], nullptr, productor, bq);pthread_join(c[0], nullptr);pthread_join(c[1], nullptr);pthread_join(p[0], nullptr);pthread_join(p[1], nullptr);pthread_join(p[2], nullptr);delete bq;return 0;return 0;
}

在这里插入图片描述

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

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

相关文章

微信小程序 工具使用(HBuilderX)

微信小程序 工具使用:HBuilderX 一 HBuilderX 的下载二 工具的配置2.1 工具 --> 设置 --> 运行配置2.1.1 微信开发者工具路径2.1.2 node 运行配置 2.2 插件 工具 --> 插件安装2.2.1 下载插件 三 微信小程序端四 同步运行五 BUG5.1 nodemon在终端无法识别 一 HBuilderX…

AnV-X6使用及总结

目录 1 简介2 安装3 基础概念3.1 画布Graph3.2 基类Cell3.3 节点Node3.4 边Edge 4 使用4.1 创建节点4.2 节点连线4.3 事件系统 5 总结 1 简介 AntV是一个数据可视化&#xff08;https://x6.antv.antgroup.com/&#xff09;的工具&#xff08;https://antv.vision/zh/ &#xf…

【深度学习实验】卷积神经网络(四):自定义二维汇聚层:最大汇聚(max pooling)和平均汇聚(average pooling)

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. Conv2D&#xff08;二维卷积层&#xff09; 2. Pool2D&#xff08;二维汇聚层&#xff09; 理论知识 a. 初始化 b. 前向传播(最大汇聚层) c. 前向传播(平均汇聚…

智能的障碍:符号化

基于事实与价值叠加的算计与基于事实的计算有着明显的区别。 基于事实的计算是指根据已有的客观事实和数据进行计算和推理。在这种计算中&#xff0c;只考虑和利用与事实相关的信息和数据&#xff0c;目的是得出合理的、基于客观事实的结论。例如&#xff0c;使用数学公式和逻辑…

CentOS密码重置

背景&#xff1a; 我有一个CentOS虚拟机&#xff0c;但是密码忘记了&#xff0c;偶尔记起可以重置密码&#xff0c;于是今天尝试记录一下&#xff0c;又因为我最近记性比较差&#xff0c;所以必须要记录一下。 过程&#xff1a; 1、在引导菜单界面&#xff08;grub&#xff…

【word密码】为什么word设置只读模式之后,还能编辑?

Word文档设置了只读模式&#xff0c;是可以编辑的&#xff0c;但是当我们进行保存的时候就会发现&#xff0c;word提示需要重命名并选择新路径才能够保存。 这种操作&#xff0c;即使可以编辑文字&#xff0c;但是原文件是不会受到影响的&#xff0c;编辑之后的word文件会保存到…

Android.bp常用语法和预定义属性

介绍 Android.bp是Android构建系统中用于定义模块和构建规则的配置文件&#xff0c;它使用一种简单的声明式语法。以下是Android.bp的一些常见语法规则和约定&#xff1a; 注释&#xff1a; 单行注释使用//符号。 多行注释使用/和/包围。 和go语言相同 // 这是单行注释 /* 这是…

Ubuntu 安装Kafka

在本指南中&#xff0c;我们将逐步演示如何在 Ubuntu 22.04 上安装 Apache Kafka。 在大数据中&#xff0c;数以百万计的数据源生成了大量的数据记录流&#xff0c;这些数据源包括社交媒体平台、企业系统、移动应用程序和物联网设备等。如此庞大的数据带来的主要挑战有两个方面…

性能压力测试的定义及步骤是什么

在今天的数字化时代&#xff0c;软件系统的性能和稳定性对于企业的成功至关重要。为了确保软件在高负载和压力情况下的正常运行&#xff0c;性能压力测试成为了不可或缺的环节。本文将介绍性能压力测试的定义、步骤。 一、性能压力测试的定义和目标 性能压力测试是通过模拟实际…

FragmentManager is already executing transactions

本文解决问题&#xff1a; java.lang.IllegalStateException: FragmentManager is already executing transactions 问题背景描述&#xff1a; 在Fragment中 用tablayoutviewpagerfragment&#xff0c;即Fragment嵌套Fragment场景、或者ViewPager2嵌套ViewPager2时。 执行生命…

vue中同一个页面参数不同动态改变数据与标题

背景 要求做一个页面&#xff0c;可以在菜单配置参数后直接跳转显示不同的报表&#xff1b; 具体步骤 步骤1&#xff1a;接收参数 参数配置与路由拦截这里不详细说&#xff0c;可以参考vue配置参数跳转 说明&#xff1a;这里参考的是saber框架中的特殊情况&#xff0c;如果…

SQLyog 连接 MySQL8.0+ 报错2058

问题如下&#xff1a; 解决方案&#xff1a; 1.首先用命令窗口进入user表 2.使用有mysql.user表权限的用户连接mysql并执行如下命令&#xff1a; ALTER USER sqlyoglocalhost IDENTIFIED WITH mysql_native_password BY root23456; 注&#xff1a;使用mysql_native_password…

DBRichEdit关联ClientDataSet不能保存的Bug

ClientDataSet的最大好处&#xff0c;就是建立能内存表&#xff0c;特别DataSnap三层运用中&#xff0c;主要使用ClientDataSet与运程的服务器中的数据表&#xff0c;建立读取存贮关系。 在软件的使用中&#xff0c;总有客户反映&#xff0c;一些数据不能保存。 发现都是使用DB…

如何使用docker快速部署MinDoc文档系统

MinDoc是非常优秀的知识分享系统&#xff0c;但是很多刚接触的人会一脸懵逼&#xff0c;而且官方文档写的也并不清晰&#xff0c;所以和大家分享一下快速部署MinDoc的方法。 首先docker环境先自行安装好&#xff0c;这里不再赘述。 拉取docker镜像&#xff1a; docker pull …

RS232,485,422的定义及区别

计算机与计算机或计算机与终端之间的数据传送可以采用串行通讯和并行通讯二种方式。 由于串行通讯方式具有使用线路少、成本低&#xff0c;特别是在远程传输时&#xff0c;避免了多条线路特性的不一致而被广泛采用。串口按电气标准及协议来划分&#xff0c;包括RS232、RS422、R…

RFID技术:钢条加工现场的智能化管理利器

RFID技术&#xff1a;钢条加工现场的智能化管理利器 RFID&#xff08;Radio Frequency Identification&#xff09;技术作为一种非接触式自动识别技术&#xff0c;近年来在工业领域得到广泛应用。本文将探讨RFID在钢条加工现场的应用&#xff0c;包括材料追踪与管理、生产过程…

杭州亚运会开幕式惊现数字人火炬手,动捕设备迸发动画制作新动能

在第十九届亚运会开幕式上&#xff0c;首次出现了“数字人”点火形式&#xff0c;打造了亚运史上首个数字点火仪式&#xff0c;这种点火方式是一种颠覆性创作的同时&#xff0c;这也是裸眼3D技术、现实增强和AI人工智能技术的完美结合。 此次数字火炬手的背后是采用了动捕设备&…

家电行业 EDI:Miele EDI 需求分析

Miele是一家创立于1899年的德国公司&#xff0c;以其卓越的工程技术和不懈的创新精神而闻名于世。作为全球领先的家电制造商&#xff0c;Miele的经营范围覆盖了厨房、洗衣和清洁领域&#xff0c;致力于提供高品质、可持续和智能化的家电产品。公司的使命是为全球消费者创造更美…

代码随想录Day03 | 链表基础1 LeetCode T203 移除链表元素 T707设计链表 T206 反转链表

本题思路和解答主要来源于: 代码随想录 (programmercarl.com) LeetCode T203 移除链表元素 题目链接:203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 首先我们回顾一下单向链表,每个链表有一个指针域和一个数据域,在内存中是呈现不连续排列的,对比之前的数组,链…

水库河道生态流量监测系统的主要内容

一、系统背景 我国为保护河流生态环境&#xff0c;推动水资源科学、合理、有序开发和可持续利用&#xff0c;各地水利和环保部门相继出台措施对不满足生态流量下泄要求的水电站责令整改或挂牌督办。近几年几百家水库在各个主要流域建成&#xff0c;由于缺乏对各个水库生态下泄流…