【Linux】生产者 消费者模型

news2024/9/24 9:28:27

文章目录

    • 1.关于模型的理解
      • 为什么会存在超市?
      • 如何维护线程互斥与同步?
        • 生产消费模型 角色之间的关系
    • 2. 交易场所的设计
      • 具体实现
        • 主函数的实现
        • BlockQueue类的实现
          • push ——生产
          • pop——消费
        • 细节问题
          • 误唤醒
          • 效率高 体现在哪里?
        • 完整代码
          • blockQueue.hpp
          • makefile
          • main.cc

1.关于模型的理解

消费者定期去超市买东西,买完在拿回来,即消费行为
供货商作为生产者,由供货商把商品生产到超市


为什么会存在超市?

消费者有可能去购买时,供货商当前并没有进行生产活动
假设要一根火腿肠,供货商不可能将机器全启动进行生产

消费者需求特别零散,供货商生产能力很强,但要考虑成本问题
所以需要超市这种零售行业,超市的存在使生产者和消费者的效率提高了

供货商可以集中生产的一大批的商品 放到超市中,让消费者随时随地来买,供货商就不生产了

因为超市的存在,允许生产和消费步调不一致


在计算机中,生产者和消费者代表线程
超市可以看作是 特定的缓冲区
生产者把自己的数据交给超市,再由消费者把数据取走 ,这种工作模式即 生产者 消费者模型

基于 生产者 消费者模型,来完成线程之间的通信

想要使用交易场所,前提是交易场所必须先被生产者和消费者线程看到
注定了 ,交易场所一定是会被多线程并发访问的公共区域,
多线程一定要保护共享资源的安全,要维护线程互斥与同步的关系

如何维护线程互斥与同步?

生产消费模型 角色之间的关系

1.生产者和生产者
生产者和生产者 为互斥关系
假设两者都要生产火腿肠,当生产者1正在生产时,生产者2也要生产就不可以


2.消费者和消费者
消费者和消费者 为 互斥关系
v假设超市货架上只有一根火腿肠了,有两个人都看上了这根火腿肠,此时两者就为竞争关系


3.生产者和消费者

生产和消费 拥有 同步关系
需要生产是先生产,需要消费是先消费
如:若超市火腿肠满了,就应该让消费者先消费,若超市没有火腿肠了,就应该让生产者先生产

生产和消费 拥有 互斥关系
假设你想在超市买一根火腿肠,正好来一个供货商
你想要在货架上拿火腿肠,供货商想要把火腿肠放到货架上,两者处于竞争状态


2. 交易场所的设计

基于阻塞队列的生产者消费者模型

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

具体实现

主函数的实现

交易场所为 阻塞队列,将模板参数定义为int,并且在堆上开辟一块空间
创建两个线程,分别为生产者和消费者,
通过调用自定义函数 consumer 执行消费任务,调用自定义函数 productor 执行生产任务

通过pthread_create ,将bq作为回调函数的参数 args ,使生产者和消费者线程看到同一个阻塞队列


productor 执行生产任务,先从某种渠道获取数据,这里使用随机数作为数据
再把数据放入 blockqueue交易场所中 ,调用blockqueue中的push


consumer 执行消费任务,先把数据从blockqueue中获取,调用blockqueue的pop
再结合业务逻辑,处理数据

BlockQueue类的实现

阻塞队列作为交易场所,有可能被多线程并发访问,
所以为了保证共享资源的安全,所以在内部添加

若队列中没有数据存在,则不该让消费者消费,若队列中数据满了,不该让生产者进行生产
但是并不知道什么时候队列为空,什么时候阻塞队列为满,从而产生饥饿问题
(不断加锁 解锁 使别人无法申请锁 ,进而无法访问临界资源)
所以也要加上条件变量

为了保证生产者和消费者互相等待,所以设置两个条件变量
consumercond 作为消费者对应的条件变量,当队列为空时,进行等待
productorcond 作为生产者对应的条件变量,当队列为满时,进行等待


push ——生产

将数据推送到lblockqueue中,调用对应BlockQueue类中的push


通过条件判断,由于队列满了,就需要当前线程进行等待 ,并自动释放锁
若队列不为满,则插入数据
关于 为什么要在申请锁之后判断 以及 wait函数第二个参数要带锁
上一篇文章都有提到,点击查看:条件变量的理解


发生休眠实际上就是线程切换,当线程从休眠状态被唤醒时,因为是从临界区被切走的,所以继续从临界区内部执行
被唤醒时,pthread_cond_wait函数处向后执行,同时又要重新申请锁,申请成功才会彻底返回

push后队列中至少有一个数据存在,所以唤醒消费者线程


pop——消费

从blockqueue中获取数据,调用对应BlockQueue类中的pop


通过条件判断,由于队列空了,就需要当前线程进行等待 ,并自动释放锁
若不为空,则删除队列数据

pop后队列中至少有一个位置为空,所以唤醒生产者

细节问题

误唤醒

假设有1个消费者以及5个的生产者
当消费者pop数据后节省出1个空间 ,错误的使用pthread_cond_broadcast 将生产者线程全部唤醒
就导致 5个生产者push 5个数据 ,
但是push 需要5个空间,而现在只有1个空间,就会超过队列的容量上限


针对上述情况,将if判断改为while循环,每一次被唤醒都要被检测,只有当队列真的是不满的情况,才会进行push

效率高 体现在哪里?

在这里插入图片描述

由于是持有锁生产的,所以生产时是不能进行消费的

当消费者在交易场所拿到数据后正在处理时,生产者可以不断的把数据放到交易场所里
处理数据和生产行为 是 并行的

当消费者从交易场所拿数据时,生产者可能不断从网络或者系统中拿数据
生产者在拿数据的过程中,并不影响消费者进行消费 ,两者同样是并行的

完整代码

blockQueue.hpp
#pragma once 
#include<iostream>
#include<pthread.h>
#include<queue>
using namespace std;

const int gcap=5;
template<class T>
class BlockQueue //阻塞队列
{
    public:
    BlockQueue(const int cap=gcap)
    :_cap(cap)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_consumercond,nullptr);
         pthread_cond_init(&_productorcond,nullptr);
    }
    ~BlockQueue()
    {
         pthread_mutex_destroy(&_mutex);
         pthread_cond_destroy(&_consumercond);
         pthread_cond_destroy(&_productorcond);
    }
      void push(const T&in)
      {
          pthread_mutex_lock(&_mutex);
          //若队列满了,就需要在条件变量中等待,并自动释放锁
          while(_q.size()==_cap)
          {
              pthread_cond_wait(&_productorcond,&_mutex);
          }
           _q.push(in);
           //队列中至少有一个数据 所以 唤醒消费者
           pthread_cond_signal(&_consumercond);
          pthread_mutex_unlock(&_mutex);
      }
      void pop( T*out)
      {
           pthread_mutex_lock(&_mutex);
           //若队列为空,则需要在条件变量中等待,并自动释放锁
           while(_q.empty())
           {
              pthread_cond_wait(&_consumercond,&_mutex);
           }
           *out=_q.front();
           _q.pop();
           //队列中至少有一个空位置  所以唤醒生产者
           pthread_cond_signal(&_productorcond);
           pthread_mutex_unlock(&_mutex);
      }
    private:
     queue<T> _q;
     int _cap;//容量
     pthread_mutex_t _mutex;//锁 提供对队列的保护
    pthread_cond_t _consumercond;//消费者对应的条件变量
    pthread_cond_t _productorcond;//生产者对应的条件变量
};
makefile
cp:main.cc
	g++ -o $@ $^ -std=c++11 -lpthread
./PHONY:clean
clean:
	rm -f cp
main.cc
#include"blockQueue.hpp"
#include<unistd.h>
#include<ctime>
 
 //消费者执行消费任务
 void*consumer(void*args)
 {
    BlockQueue<int>*bq=(BlockQueue<int>*)args;
    while(true)
    {
      sleep(1);
        int data=0;
        //1.将数据从blockqueue中获取 --获取到了数据
         bq->pop(&data);
        //2.结合某种业务逻辑,处理数据
        cout<<"consumer data :"<<data<<endl;
    }
 }

//生产者执行生产任务
 void*productor(void*args)
 {

  BlockQueue<int>*bq=(BlockQueue<int>*)args;
  while(true)
  {
    //1.通过某种渠道获取数据
     int data=rand()%10+1;//1-10
    //2.将数据推送到blockqueue中  --- 完成生产的任务  
      bq->push(data); 
      cout<<"productor data :"<<data<<endl;
  }
 }
 int main()
 {
    srand((uint64_t)time(nullptr)^ getpid());//随机数
   BlockQueue<int> *bq= new  BlockQueue<int>;

   //单生产和单消费
   pthread_t c,p;
   pthread_create(&c,nullptr,consumer,bq);//消费者
   pthread_create(&p,nullptr,productor,bq);//生产者

   pthread_join(c,nullptr);
   pthread_join(p,nullptr);
   delete bq;
    return 0;
 }


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux之动态库和静态库

文章目录 前言一、动态库和静态库概念二、库三、制作静态库1. 创建Makefile&#xff1a;2.打包库3.使用库总结注意库的安装 四、制作动态库总结 五、动静态库的加载总结 前言 一、动态库和静态库概念 在之前的文章中&#xff0c;介绍过动静态库的概念&#xff0c;因此这次我们…

微信小程序面试题汇总

HTML篇CSS篇JS篇Vue篇TypeScript篇React篇前端面试题汇总大全&#xff08;含答案超详细&#xff0c;HTML,JS,CSS汇总篇&#xff09;-- 持续更新前端面试题汇总大全二&#xff08;含答案超详细&#xff0c;Vue&#xff0c;TypeScript&#xff0c;React&#xff0c;Webpack 汇总篇…

虎牙在全球 DNS 秒级生效上的实践2

博主介绍&#xff1a;✌全网粉丝4W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战、定制、远程&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面…

pytest使用手册

1. pytest寻找测试项的具体规则 如果未指定命令行参数&#xff0c;则从pytest命令运行的当前目录开始收集。如果在命令行参数中指定了目录、文件名则按参数来寻找。寻找过程会按照目录层层递归&#xff0c;在这些目录中&#xff0c;搜索 test_*.py 或 *_test.py 文件。从这些文…

GPT-4变笨引爆舆论!文本代码质量都下降,OpenAI刚刚回应了降本减料质疑

梦晨 克雷西 发自 凹非寺 量子位 | 公众号 QbitAI 大模型天花板GPT-4&#xff0c;它是不是……变笨了&#xff1f; 先是少数用户提出质疑&#xff0c;随后大量网友表示自己也注意到了&#xff0c;还贴出不少证据。 有人反馈&#xff0c;把GPT-4的3小时25条对话额度一口气用完…

PDF怎么转换成WORD?分享这几个方法给大家!

PDF怎么转换成Word&#xff1f;在我们的工作过程中&#xff0c;经常会使用到PDF文件、Word文件等等。而在很多时候&#xff0c;需要根据工作需求&#xff0c;将各种文件进行格式转换&#xff0c;例如将PDF文件转换成Word格式&#xff0c;从而满足我们对文件进行编辑、更改等需求…

learn C++ NO.8——初识模板(函数模板、类模板)

文章目录 引言1.泛型编程1.1.什么是泛型编程&#xff1f; 2.函数模板2.1.什么是函数模板2.2.为什么需要函数模板2.3.函数模板格式2.4.函数模板实现原理2.5.函数模板的实例化 3.类模板3.1.类模板定义格式3.1.1.类模板语法3.1.2.模板类的定义 3.2.模板类的实例化 引言 现在是北京…

Hadoop之Yarn概述

Hadoop之Yarn概述 Yarn是什么Yarn基础架构Yarn工作机制回顾HDFS、YARN、MapReduce三者关系Yarn调度器和调度算法先进先出调度器&#xff08;FIFO&#xff09;容量调度器&#xff08;Capacity Scheduler&#xff09;公平调度器&#xff08;Fair Scheduler&#xff09; Yarn常用命…

SpringBoot整合SpringSession实现分布式登录详情

目录 Session 共享为什么服务器 A 登录后&#xff0c;请求发到服务器 B&#xff0c;不认识该用户&#xff1f;解决方案SpringBoot整合SpringSession实现分布式登录 Session 共享 比如两个域名&#xff1a; aaa.yupi.combbb.yupi.com如果要共享 cookie&#xff0c;可以种一个…

事件机制原理剖析及实际业务应用说明

什么是事件&#xff1f; 一个特定的场景发生了一个特定的情况就是一个事件。 事件在设计中的作用 为对象之间解耦。 举例 现有用户中心和消息中心。 目前&#xff0c;有一个用户注册的场景&#xff0c;此场景要求用户注册成功后要给用户发送多渠道欢迎通知&#xff08;微信、…

(11) XGBoost

文章目录 1 简要介绍2 梯度提升树2.1 提升集成算法&#xff1a;重要参数n_estimators2.2 有放回随机抽样&#xff1a;重要参数subsample2.3 迭代决策树&#xff1a;重要参数 η \eta η 3 XGBoost的智慧3.1 选择弱评估器&#xff1a;重要参数booster3.2 目标函数&#xff1a;重…

SpringCloud_微服务基础day2(Eureka注册中心:服务注册与发现

p6:Eureka简介与依赖导入 前面我们了解了如何对单体应用进行拆分&#xff0c;并且也学习了如何进行服务之间的相互调用&#xff0c;但是存在一个问题&#xff0c;就是虽然服务拆分完成&#xff0c;但是没有一个比较合理的管理机制&#xff0c;如果单纯只是这样编写&#xff0c…

HBase:(三)HBase API

HBase:(一)安装部署_只爱大锅饭的博客-CSDN博客hbase部署安装https://blog.csdn.net/qq_35370485/article/details/130988364?spm1001.2014.3001.5501 1.创建maven项目 2.添加依赖 <dependency><groupId>org.apache.hbase</groupId><artifactId>hba…

【鲁棒】对信息不完整的 DSGE 模型进行鲁棒预测(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

使用 TiUP 部署 TiDB 7.1.0 集群

使用 TiUP 部署 TiDB 7.1.0 集群 参考官方文档 1. 下载社区版安装包 TiDB 社区版 TiDB 7.1.0 为长期支持版本 (Long-Term Support Releases, LTS)。 TiDB-community-server 软件包 TiDB-community-toolkit 软件包 wget https://download.pingcap.org/tidb-community-ser…

【论文阅读】Attention-based Deep Multiple Instance Learning

misc{Ilse_Tomczak_Welling_2018, title{Attention-based Deep Multiple Instance Learning}, journal{International Conference on Machine Learning}, author{Ilse, Maximilian and Tomczak, JakubM. and Welling, Max}, year{2018}, month{Jul} }1、摘要与引言 本文…

CodeForces..走路的男孩.[简单].[时间间隔]

题目描述&#xff1a; 题目解读&#xff1a; 数学上的遛狗问题。 大意就是需要遛狗&#xff0c;一天至少两次&#xff0c;每次至少120分钟&#xff0c;题中所给的时间轴是从0-1440分钟&#xff0c;表示一整天。 然后它的主人正在不断接收信息&#xff08;在工作&#xff09;…

作用域 (局部作用域和全局作用域) 详细介绍

作用域 (局部作用域和全局作用域) 详细介绍 作用域是当前的执行上下文&#xff0c;值和表达式在其中“可见”或可被访问。 常见的作用域为&#xff1a; 全局作用域&#xff1a;脚本模式运行所有代码的默认作用域 函数作用域&#xff1a;由函数创建的作用域 局部作用域&#xff…

附录1-小程序常用标签

目录 1 view 2 scroll-view 3 swiper与swiper-item 4 text 5 rich-text 6 button 7 image 1 view 相当于html的div 2 scroll-view scroll-view是一个有滚动条的div scroll-y是允许纵向滚动&#xff0c;scroll-x是允许横向滚动 3 swiper与swiper-item swipe…

文盘Rust -- tokio绑定cpu实践 | 京东云技术团队

tokio 是 rust 生态中流行的异步运行时框架。在实际生产中我们如果希望 tokio 应用程序与特定的 cpu core 绑定该怎么处理呢&#xff1f;这次我们来聊聊这个话题。 首先我们先写一段简单的多任务程序。 use tokio::runtime; pub fn main() {let rt runtime::Builder::new_mu…