Linux多线程之生产者消费者模型1

news2024/10/5 16:30:58

目录

🍊一、什么是生产者消费者模型

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

🍊三、生产消费模型的upgrade版本

🍊 四、三线程实现生产消费和存储

🍊一、什么是生产者消费者模型

生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而是通过阻塞队列来进行通讯。所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列。消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列是用来给生产者和消费者解耦的。

 类比到生活,生产者类似于供应商,消费者类似于学生之类的消费者,而他们中间还有一个缓冲区,就是超市。供应商(生产者)不直接供应商品给消费者,而是供应给超市,消费者从超市买商品。

其中,生产者和生产者之间是互斥关系。消费者和消费者之间是互斥关系。生产者和消费者之间是互斥关系(当生产者和消费者访问同一份资源,在同一时间必须保证只有一个在访问)。而同步关系就是当有货时通知消费者来消费,当货少时通知生产者生产。

总结:"321"原则

3种关系:生产者和生产者(互斥),消费者和消费者(互斥),生产者和消费者(互斥(保证共享资源的安全性)&&同步)

2种角色:生产者线程,消费者线程

1个交易场所:一段特定结构的缓冲区

特点:

1、生产线程和消费线程进行解耦

2、支持生产和消费的一段时间的忙闲不均的问题

3、提高效率

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

在多线程编程中阻塞队列(Blocking Queue)是一种常用于实现生产者和消费者模型的数据结构。其与普通的队列区别在于,当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中被放了元素;当队列满时,往队列里存放元素的操作也会被阻塞,直到有元素被从队列中取出

 

C++ queue模拟阻塞队列的生产消费模型

代码:为了方便理解,这里先以单生产者,单消费者来进行讲解。

 

 

这个模型是简单的单生产单消费模型,而且生产消费的是int类型简单数据。后序我们会把他升级,可以生产消费一个类。

 

通过演示生产消费模型,观察发现,生产数据和消费数据一模一样,这是因为生产数据时我们让它每生产一个数据,sleep(1),而消费数据比较快。当生产完通知消费时,取的都是最新生产的数据,生产一个消费一个。

而如果我们让生产的快,消费的慢,就会发现一瞬间先生产满空间,然后趋于稳定生产一个消费一个,而消费的数据是历史队列上前面的数据,因为队列是FIFO的。

 

 细节1

但是我们要注意一个细节,当生产者被条件变量阻塞时,它是带着锁被阻塞的。而我们的生产者和消费者使用的是同一个锁,按照以前的知识,锁只能被一个线程占有,但是这里生产被阻塞时并没有影响消费者消费数据,这是为什么?

这里就要谈到pthread_cond_wait()的第二个参数必须是我们正在使用的互斥锁。pthread_cond_wait()该函数在调用的时候,会以原子性的方式将锁释放,并将自己挂起。该函数在被唤醒返回的时候会自动地重新获取你传入的锁。这就是我们为什么不担心锁的问题。

细节2

充当条件判断的语法必须是while,不能是if

 原因是我们的代码唤醒是一次唤醒一次生产。如果此时我们的生产数据只有一份空缺,而消费数据时代码中唤醒不是一次生产(pthread_cond_signal),而是全部唤醒(pthread_cond_broadcast),如果是if条件判断,他们会接着之前的代码往下执行,这时多个线程生产数据而空缺只有一份,会出现生产错误!唤醒后需要再次判断(提高代码的健壮性)。

细节3

pthread_cond_signal()这个函数可以放在临界区内部,也可以放在临界区外部。

所以可以先解锁再唤醒。

 因为一旦唤醒,生产者或者消费者就会重新拿回锁,而这时消费者或者生产者还没有解锁,需要等待他们解锁,而先解锁再唤醒就不需要等待了。

基于这些,我们再对代码进行修改,生产消费类而不是简单的int。

🍊三、生产消费模型的upgrade版本

 

 

 BlockQueue.hpp中写了一个阻塞队列,阻塞队列的私有成员变量是一把锁和生产者和消费者的条件变量以及一个队列和队列最大容量。阻塞队列中主要成员函数是push()和pop(),push()是生产者调用的函数,它用于生产任务到队列中,而pop()是消费者调用的函数,用于处理阻塞队列中的任务然后将处理过的任务pop出队列。Task.hpp中是对任务封装的一个类,它的成员是两个操作数一个操作符以及一个回调函数,这个任务类是阻塞队列的基本元素。Main.Cp.cc中是主函数,它的逻辑就是创建两个线程,一个是生产者线程,一个是消费者线程。

🍊 四、三线程实现生产消费和存储

实现不同线程执行不同任务:生产者派发任务,消费处理任务,记录任务结果(将结果记录在文件中)。

 p线程生产,并将任务存储到计算队列中

c线程从计算队列中获取任务,然后处理任务,并将处理结果保存到保存队列中

s线程从保存队列中获取处理结果,并将结果保存到文件中。

代码:

 

 

 

 上面只是一个生产者,一个消费者,一个存储者。可以改成多生产,多消费的模型。

 

 可以实现多生产多消费。

无论生产还是消费,因为是加锁的,会进入阻塞队列,所以任意时刻只有一个线程生产或者消费,是线程安全的!

 🖊生产消费模型高效在哪里?

对生产者而言,向blockqueue里面放置任务,而对消费者而言,是从blockqueue里面拿取任务。

那么生产者的任务从哪里来?生产者获取任务和构建任务要不要花时间?对于消费者,难道它把任务从任务队列中拿出来就结束了吗?消费者拿到任务之后,后续还有没有任务?

🍊都是需要花费时间的!如果生产者是从数据库等中获取数据,十分消耗时间,多线程生产者的好处在于这样当一个线程在获取构建任务时会有其他生产者线程进行生产之前的处理。同理,如果消费者处理任务也很消耗时间,单线程消费者处理任务耗时比较长,多线程的好处就是可以在消费之后,线程并行执行,提高效率。

多个线程并发处理,并不互相影响。生产者和消费者模型并不高效在加锁,它一次还是只能让一个线程处理。而是在生产之前和消费之后,可以让线程并行执行!

类似于虽然生产和消费的过程是加锁的,但是准备工作是可以并行执行的,比如生产饺子是加锁的,但是准备饺子馅,饺子皮(生产之前)和对饺子进行包装(消费处理之后)是可以提前并发准备的!

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

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

相关文章

SciencePub学术 | 智能计算类重点SCIEEI征稿中

SciencePub学术 刊源推荐: 智能计算类重点SCI&EI征稿中!2区闭源正刊,对国人友好!信息如下,录满为止: 一、期刊概况: 智能计算类重点SCIE&EI 📌【期刊简介】IF:8.0-8.5&…

FormData 介绍和使用

FormData 是 JavaScript 中用于处理表单数据的接口。它提供了一种简单的方式来构建和发送表单数据,表单数据以键值对的形式向服务器发送,这个过程是浏览器自动完成的。但是有时候,我们希望通过脚本完成这个过程,构造或编辑表单的键…

生态工具箱 | 虚拟机测试工具WasmFuzzer,智能合约安全防火墙

长安链生态工具箱 丰富实用的区块链生态工具不仅可以让开发者部署、开发过程更加得心应手,还可以从能力上扩展区块链应用边界。长安链正在构建强大的生态工具箱以增强在其在各类场景下的应用能力,如智能合约漏洞检测、抗量子多方安全计算、链迁移、密…

CaffeineCache+Redis 接入系统做二层缓存思路实现(借鉴 mybatis 二级缓存、自动装配源码)

本文目录 前言本文术语本文项目地址设计思路开发思路DoubleCacheAble 双缓存注解(如何设计?)动态条件表达式?例如:#a.id?(如何解析?)缓存切面(如何设计?&…

Linux centos7下漏洞扫描工具 Nessus8.15.9的下载、安装

一、下载Nessus 传送带地址:Download Nessus | Tenable 因为Darren洋的Linux操作系统是Linux Centos7 64 位,大家可以根据自己的选择合适的系统版本,在linux系统中用以下命令即可完成查询系统版本。 cat /etc/redhat-release 二、安装Ness…

Axure8 基本操作记录

参考:黑马产品经理课程 视频资源:day1&day2,Axure部分 文章小结图片 Axure8常用功能 选择/缩放 选择 包含选中:全部选中才有效(避免误操作,建议使用这个)相交选中:相交即全选中…

同时安装vue-cli2和vue-cli3

同时安装vue-cli2和vue-cli3 发布时间环境安装后的效果安装vue-cli2安装vue-cli3vue-cli3和vue-cli2的区别vue-cli2目录结构vue-cli3目录结构 发布时间 vue版本发布时间Seed.js2013年vue最早版本最初命名为Seedvue-js 0.62013年12月更名为vuevue-js 0.82014年1月对外发布vue-j…

vue2 用watch监听props 失效,解决办法

这个是父组件传递下来的props 这样子好像TCshow的值并没有赋上 必须修改成下面这种:

[golang 微服务] 7. go-micro框架介绍,go-micro脚手架,go-micro结合consul搭建微服务案例

一.go-micro框架 前言 上一节讲解了 GRPC微服务集群 Consul集群 grpc-consul-resolver相关的案例,知道了微服务之间通信采用的 通信协议,如何实现 服务的注册和发现,搭建 服务管理集群,以及服务与服务之间的 RPC通信方式,具体的内容包括: pro…

SpringBoot 如何使用 IOC 容器

SpringBoot 如何使用 IOC 容器 Spring 是一个非常流行的 Java 开发框架,它提供了一个强大的 IoC(Inversion of Control)容器来管理 Java 对象之间的依赖关系。在 SpringBoot 中,我们可以非常方便地使用这个 IoC 容器来管理我们的…

骨传导耳机音质怎么样,几款解析力度不错的骨传导耳机分享

​骨传导耳机在之前的时候一直是“冷门”的,但是随着技术的进步,现在骨传导耳机也逐渐被大家所熟知。对于喜欢运动和健身的人来说,骨传导耳机可以避免佩戴普通耳机导致耳朵疼痛的情况。因此,目前在市面上很多骨传导耳机都很受欢迎…

Git教程(快速上手,超详细)

文章目录 版本控制Git环境配置Git基本理论Git项目搭建Git文件操作使用码云IDEA集成GitGit分支 版本控制 版本迭代:每次更新就会有新的版本,旧的版本需要保留。所以我们需要一个版本控制工具帮助我们处理这个问题 版本控制(Revision control)是…

入门学习编码器与自编码器1----包括详细的理论讲解与详细的python程序代码,小白直接看懂!!!纯干货

文章目录 前言--为什么要学习编码器和自编码器?一、编码器与自编码器究竟是什么?二、下面是一个简单的Python实现自编码器的示例三、程序运行结果四、查看模型结构总结 前言–为什么要学习编码器和自编码器? 学习编码器和自编码器可以帮助我…

【数据分享】1929-2022年全球站点的逐月平均风速数据(Shp\Excel\12000个站点)

气象数据是在各项研究中都经常使用的数据,气象指标包括气温、风速、降水、能见度等指标,说到气象数据,最详细的气象数据是具体到气象监测站点的数据! 对于具体到监测站点的气象数据,之前我们分享过1929-2022年全球气象…

「你将购买的是虚拟内容服务,购买后不支持退订」,真的合理么?

编辑导语:你是否也有见过相似提示,即虚拟内容服务购买之后不予退款?那么你有想过,在这一规定背后,其制约因素都有什么吗?这一规定是合理的吗?用户若真的有退款需求,产品上是否能实现…

卷积计算加速方法--分块卷积1

文章目录 1、大尺寸卷积存在的问题2、分块卷积overlap产生的来源3、分块卷积overlap的计算4、结论及加速效果 1、大尺寸卷积存在的问题 当卷积的输入太大导致内存不够用时,考虑将一大块卷积分成多个小块分别进行卷积,相当于将原始输入分成几个小的输入经…

【C++】C++11:线程库和包装器

C11最后一篇文章 文章目录 前言一、线程库二、包装器和绑定总结 前言 上一篇文章中我们详细讲解了lambda表达式的使用,我们今天所用的线程相关的知识会大量的用到lambda表达式,所以对lambda表达式还模糊不清的可以先将上一篇文章看明白。 一、线程库 在…

域名解析详解

域名解析 记录类型: 提示: 将域名指向云服务器,选择 A; 将域名指向另一个域名,选择 CNAME; 建立邮箱选择 MX,根据邮箱服务商提供的 MX 记录填写。 记录类型解释A用来指定域名的 IPv4 地址&…

燃气管网监测设备:燃气管网压力在线监测

燃气作为一种重要的能源,广泛用于家庭、工业和商业领域。然而,燃气管网系统在运输和分配过程中可能面临压力波动、管道老化、外部破坏等问题,可能导致燃气泄漏和事故发生。燃气管网压力在线监测是保障燃气管网安全运营的重要手段之一。通过燃…

Linux系统之部署Homepage个人导航页

Linux系统之部署Homepage个人导航页 一、Homepage介绍1.1 Homepage简介1.2 Homepage主要特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、检查本地环境3.1 检查本地操作系统版本3.2 检查系统内核版本3.3 检查系统是否安装Node.js 四、部署Node.js 环境4.1 下载Node…