【仿RabbitMQ消息队列】基于C++11中packaged_tack异步线程池
目录
什么是同步和异步?
future
使用future和async配合管理异步任务
使用promise和future配合管理异步任务
使⽤std::packaged_task和std::future配合
C++11异步线程池
什么是同步和异步?
同步(Synchronous)
同步编程是指程序按照代码的顺序,一步步执行,每一步必须等待上一步执行完毕,才能执行下一步。在同步操作中,如果某个任务(如I/O操作、网络请求等)需要等待(比如等待磁盘读写完成、等待网络响应等),程序就会阻塞在那里,直到该任务完成,才能继续执行后续的代码。这种方式简单直观,但效率较低,尤其是在处理需要长时间等待的任务时,会导致程序响应变慢或“假死”。
异步(Asynchronous)
异步编程则允许程序在等待某个长时间运行的操作(如文件I/O、网络请求等)完成时,继续执行后续的代码,而不会阻塞当前线程。当异步操作完成时,通常通过回调函数、Promises、Future、async/await等方式来通知程序,以便执行后续依赖于该操作结果的操作。这种方式可以提高程序的效率和响应性,尤其是在处理多个并发任务时。
future
介绍
std::future是C++11标准库中的⼀个模板类,它表⽰⼀个异步操作的结果。当我们在多线程编程中使⽤异步任务时,std::future可以帮助我们在需要的时候获取任务的执⾏结果。std::future的⼀个重要特性是能够阻塞当前线程,直到异步操作完成,从⽽确保我们在获取结果时不会遇到未完成的操作。
简单来说:future就是可以用来获取一个异步任务/函数的结果的;
应用场景:
- 异步任务: 当我们需要在后台执⾏⼀些耗时操作时,如⽹络请求或计算密集型任务等,std::future可以⽤来表⽰这些异步任务的结果。通过将任务与主线程分离,我们可以实现任务的并⾏处理,从⽽提⾼程序的执⾏效率
- 并发控制: 在多线程编程中,我们可能需要等待某些任务完成后才能继续执⾏其他操作。通过使⽤std::future,我们可以实现线程之间的同步,确保任务完成后再获取结果并继续执⾏后续操作
- 结果获取:std::future提供了⼀种安全的⽅式来获取异步任务的结果。我们可以使⽤std::future::get()函数来获取任务的结果,此函数会阻塞当前线程,直到异步操作完成。这样,在调⽤get()函数时,我们可以确保已经获取到了所需的结果
使用future和async配合管理异步任务
std::async是⼀种将任务与std::future关联的简单⽅法。它创建并运⾏⼀个异步任务,并返回⼀个与该任务结果关联的std::future对象。默认情况下,std::async是否启动⼀个新线程,或者在等待future时,任务是否同步运⾏都取决于你给的 参数。这个参数为std::launch类型:
- std::launch::deferred 表明该函数会被延迟调⽤,直到在future上调⽤get()或者wait()才会开始执⾏任务
- std::launch::async 表明函数会在⾃⼰创建的线程上运⾏
- std::launch::deferred | std::launch::async 内部通过系统等条件⾃动选择策略
#include <iostream>
#include <thread>
#include <future>
#include<chrono>
using namespace std;
int Add(int num1, int num2)
{cout << "加法" << endl;return num1 + num2;
}
int main()
{// 两个版本// 函数+不定参数// 策略+函数+不定餐cout<<"first "<<endl;//deferred: 在执行get获取异步结果的时候才会执行异步任务//async: 内部会创建工作线程异步完成任务future<int> result = async(std::launch::deferred, Add, 11, 22);// 返回值是什么参数就是什么std::this_thread::sleep_for(chrono::seconds(1));cout<<"two"<<endl;int sum=result.get();cout<<"three"<<endl;cout<<sum<<endl;return 0;
}
使用promise和future配合管理异步任务
std::promise提供了⼀种设置值的⽅式,它可以在设置之后通过相关联的std::future对象进⾏读取。换种说法就是之前说过std::future可以读取⼀个异步函数的返回值了, 但是要等待就绪, ⽽std::promise就提供⼀种 ⽅式⼿动让 std::future就绪
我们可以把promise理解为主线程给另一个执行异步任务函数线程放返回值的篮子,但是这个篮子交给另一个线程后无法在给回来,因此主线程需要用promise创建一个future工具从这个篮子中去取返回值
#include<iostream>
#include<thread>
#include<future>
using namespace std;
void Add(int num1,int num2,promise<int> &prom)
{prom.set_value(num1+num2);return ;
}
int main()
{promise<int> prom;future<int> fu=prom.get_future();thread t1(Add,11,22,ref(prom));int res = fu.get();cout<<"sum: "<<res<<endl;t1.join();return 0;
}
使⽤std::packaged_task和std::future配合
std::packaged_task就是将任务和 std::feature 绑定在⼀起的模板,是⼀种对任务的封装。我们可以通过std::packaged_task对象获取任务相关联的std::feature对象,通过调⽤get_future()⽅法获得。std::packaged_task的模板参数是函数签名。
可以把std::future和std::async看成是分开的, ⽽ std::packaged_task则是⼀个整体。
#include<iostream>
#include<thread>
#include<future>
#include<memory>
using namespace std;
//package_task是一个模板类,实例化的对象可以对一个函数进行二次封装
int Add(int num1,int num2)
{return num1+num2;
}
int main()
{auto ptask = make_shared<packaged_task<int(int,int)>>(Add);future<int> fu = ptask->get_future();thread thr([ptask](){(*ptask)(11,22);});int sum = fu.get();cout<<sum<<endl;thr.join();return 0;
}
C++11异步线程池
对于普通的线程池我们只是将任务交给线程执行即可,但是如果我们想要获取任务执行的返回值就要进行类似上面的异步处理;基于线程池执⾏任务的时候,⼊⼝函数内部执⾏逻辑是固定的,因此选择std::packaged_task加上std::future的组合来实现。
线程池工作思想
⽤⼾传⼊要执⾏的函数,以及需要处理的数据(函数的参数),由线程池中的⼯作线程来执⾏函
数完成任务
实现
管理成员
- 任务池:⽤vector维护的⼀个函数任务池⼦
- 互斥锁 & 条件变量: 实现同步互斥
- ⼀定数量的⼯作线程:⽤于不断从任务池取出任务执⾏任务
- 结束运⾏标志:以便于控制线程池的结束
管理的操作
- ⼊队任务:⼊队⼀个函数和参数
- 停⽌运⾏:终⽌线程池
完整代码
class threadpool
{
public:using Functor = std::function<void(void)>;threadpool(int thr_count = 1) : _stop(false){for (int i = 0; i < thr_count; i++){_threads.emplace_back(&threadpool::entry, this);}}~threadpool(){stop();}void stop(){if (_stop == true)return;_stop = true;_cv.notify_all();// 等待所有线程退出for (auto &thread : _threads){thread.join();}}// push传入的首先有一个函数// 不定参数表示要传入函数的参数// push函数内部会将这个传入的函数封装成一个异步任务,抛入任务池中,由工作线程取出执行// 使用lambda表达式生成一个可调用对象,抛入到任务池中,由工作线程取出进行执行// 入队// 函数类型// 参数包template <typename F, typename... Args>auto push(F &&func, Args &&...args) -> std::future<decltype(func(args...))>{// 1.将传入的函数封装一个packaged_task任务using return_type = decltype(func(args...)); // 退出函数返回值对象auto tmp_func = std::bind(std::forward<F>(func), std::forward<Args>(args)...);auto task = std::make_shared<std::packaged_task<return_type()>>(tmp_func);std::future<return_type> fu = task->get_future();// 2。构造一个lambda表达式(捕获任务对象),函数内执行任务对象{std::unique_lock<std::mutex> lock(_mutex);// 3.将构造出来的匿名函数对象,抛入到任务池中_taskpool.push_back([task](){ (*task)(); });_cv.notify_one();}return fu;}private:// 线程入口函数// 不断地从任务池中取任务进行执行void entry(){while (!_stop){std::vector<Functor> tem_taskpool;{// 加锁std::unique_lock<std::mutex> lock(_mutex);// 等待:等待任务池不为空,或者_stop被置位_cv.wait(lock, [this](){ return _stop || !_taskpool.empty(); });// 取出任务执行tem_taskpool.swap(_taskpool);}for (auto &task : tem_taskpool){task();}}}private:std::atomic<bool> _stop;std::vector<Functor> _taskpool; // 任务池std::mutex _mutex;std::condition_variable _cv;std::vector<std::thread> _threads; // 线程池
};
上面异步线程池的完整代码中的语法规则都是C++11的新特性,在核心功能中甚至一行都有好几个的C++11新特性,要想理解上面的代码需要我们非常熟悉新特性;
今天对C++11异步线程池的分享到这就结束了,希望大家读完后有很大的收获,也可以在评论区点评文章中的内容和分享自己的看法;个人主页还有很多精彩的内容。您三连的支持就是我前进的动力,感谢大家的支持!!!
相关文章:
【仿RabbitMQ消息队列】基于C++11中packaged_tack异步线程池
目录 什么是同步和异步? future 使用future和async配合管理异步任务 使用promise和future配合管理异步任务 使⽤std::packaged_task和std::future配合 C11异步线程池 什么是同步和异步? 同步(Synchronous) 同步编程是指程…...

免费下载专利
给大家提供一个可以免费下载专利的地方 链接:https://www.drugfuture.com/cnpat/cn_patent.asp...
CentOS7安装流程步骤详细教程
1. 简介 1.1. 概述 CentOS 7是一款基于Red Hat Enterprise Linux(RHEL)的开源Linux发行版,它提供了一个稳定、安全、高效的操作系统,适用于企业级应用、服务器、云计算等领域。CentOS 7以其高度的稳定性和安全性而闻名,它广泛应用于各种行业和领域,包括政府、金融、医疗、…...

【大模型从入门到精通17】openAI API 构建和评估大型语言模型(LLM)应用5
这里写目录标题 理论问题:实践问题:理论实践 理论问题: 1.描述评估LLM应用程序输出的重要性,并提及至少三个维度,这些输出应该在这几个维度上被评估。 2.解释在评估LLM应用程序时开发稳健的性能指标的作用,…...

苹果手机无iCloud备份下“最近删除”照片的恢复策略
iPhone最近删除清空了照片还能恢复吗?大家都知道,照片对于我们来说是承载着美好回忆的一种形式。它记录着我们的平淡生活,也留住了我们的美好瞬间,因此,具有极其重要的纪念价值。但由于我们的失误操作导致照片被删除&a…...

Docker搭建Minio容器
Docker搭建Minio容器 前言 在上一集我们介绍了分布式文件存储行业解决方案以及技术选型。最终我们决定选用Minio作为分布式文件存储。 那么这集我们就在Docker上搭建Minio容器即可。 Docker搭建Minio容器步骤 创建Minio文件目录 我们选择创建/minio/data目录 修改目录权…...

【C++】多源BFS问题和拓扑排序
目录 多源BFS介绍 单源BFS和多源BFS的区别 SO如何解决多源BFS问题 多源之核心 矩阵 算法思路 代码实现 飞地的数量 算法思路 代码实现 地图中的最高点 算法思路 代码实现 地图分析 算法思路 代码实现 拓扑排序介绍 有向无环图 编辑 如何解决这类问题 课…...

CentOS 7 安装详细教程
大家好,我是程序员小羊! 前言: CentOS 7 是一个稳定的企业级 Linux 发行版,广泛用于服务器环境。CentOS 7 是基于 Red Hat Enterprise Linux (RHEL) 构建的企业级 Linux 发行版,提供免费的、开源的操作系统…...
mybatis-plus + springboot 多对多实例
在MyBatis Plus中,自动填充数据是一种非常实用的功能,它可以自动地为一些字段设置默认值,比如创建时间和更新时间。对于多对多关系来说,虽然自动填充主要针对单一实体的字段,但在某些情况下,你可能也需要在…...
SpringBoot日志整合
Spring Boot 整合日志框架的核心是通过 spring-boot-starter-logging 依赖来实现的,它默认整合了 Logback 日志框架。 Spring Boot 对各种日志框架进行了自动配置,使得我们可以很容易地在 Spring Boot 应用中使用日志。 Spring Boot 在类路径下寻找 Log…...

信创教育:培养未来科技创新的生力军
随着全球数字化转型的加速,信息技术应用创新(简称“信创”)产业作为推动国家信息技术自主可控和产业升级的关键领域,正迎来前所未有的发展机遇。信创教育,作为培养未来科技创新生力军的重要阵地,其重要性和…...

slowfast
核心网络网络架构: 1、分别获取高频和低频图像数据 2、分别进行特征提取 3、特征融合 4、预测 网络结构细节:...

怎么调试python脚本
打开pycharm community 2019.1软件,创建一个项目。 创建一个py后缀的文件作为示范,文件名自己定义。 编写代码,然后右键点击进行运行,查看一下是否有问题。 点击右上角的虫子图标,然后下面会有控制面板出来,…...
Flask获取请求信息
示例代码 from flask import Flaskapp = Flask(__name__)if __name__ == "__main__": app.run(debug=True) 1、获取请求头 from flask import request@app.route("/headers", methods=["GET"])def get_headers(): headers = request.heade…...
Overleaf中放置高分辨率图片的方法
如果将ppt中的图片另存为png或jpg等格式,如果图中有密集的编码网格,则生成的pdf会糊掉。如何确保生成的pdf中的图片放大后仍然保持细节?亲测方案: 1、将ppt中的图片尺寸记下来,然后在ppt→设计中将ppt模板的大小设置成…...

【C语言】动态内存管理(malloc,free,calloc,realloc详解 )
🦄个人主页:小米里的大麦-CSDN博客 🎏所属专栏:https://blog.csdn.net/huangcancan666/category_12718530.html 🎁代码托管:C语言: C语言方向(基础知识和应用) (gitee.com) ⚙️操作环境:Visual Studio 2022 目录 一、…...

如何寻找数值仿真参数最优解?CFD参数优化详解3来袭
本期文章将通过2个简单案例演示参数优化的操作步骤,一起来看看吧! 流程自动化 实现 CFD 参数优化,首先要创建流程自动化。用户可采用SimLab的Python宏命令,录制建模流程。或在HyperWorks CFD模块的Template Manager创建Tcl/Tk命令…...

虚拟机macos中构建llvm、clang并配置Xcode
安装虚拟机macos,并安装brew: 安装vmware:https://www.bilibili.com/video/BV1Wo4y1E7fc/安装最新版的macos:极限苹果-Mac论坛-提供Mac软件和macOS苹果系统镜像下载下载并安装brew:版本要低,我装的是4.3.5…...
Java 中的 @SneakyThrows 注解详解:简化异常处理的利与弊
在 Java 开发中,异常处理是一个不可避免的重要部分。我们经常需要处理各种检查型异常(checked exceptions),这有时会导致代码变得冗长且难以维护。为了简化异常处理,Lombok 提供了一个强大的注解——SneakyThrows。本文…...

系统编程 day11 进程(线程)3
fork函数的总结: 总结对进程学习之中的回收函数wait wait函数: 1.获取子进程的退出状态 2.回收资源------会让僵尸态的子进程销毁 注:1.wait函数本身是一个阻塞操作,会使调用者阻塞 2.父进程要获得子进程的退出状态 子进程&…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...

企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...

基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
【WebSocket】SpringBoot项目中使用WebSocket
1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖,添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...
【实施指南】Android客户端HTTPS双向认证实施指南
🔐 一、所需准备材料 证书文件(6类核心文件) 类型 格式 作用 Android端要求 CA根证书 .crt/.pem 验证服务器/客户端证书合法性 需预置到Android信任库 服务器证书 .crt 服务器身份证明 客户端需持有以验证服务器 客户端证书 .crt 客户端身份…...