异步操作实现线程池
文章目录
- future
- async
- promise
- package task
- C++11线程池实现
future
在C++11标准库中,提供了一个future的模板类,它表示的是一个异步操作的结果,当在多线程编程中使用异步任务的时候,使用这个类可以帮助在需要的时候获取到对应的数据处理结果,而future类本质上的一个重要特性是可以阻塞当前线程,直到异步操作完成,而确保在获取结果的时候不会出现结果未完成的情况出现
应⽤场景
-
异步任务:当我们需要在后台执⾏⼀些耗时操作时,如⽹络请求或计算密集型任务等,std::future
可以⽤来表⽰这些异步任务的结果。通过将任务与主线程分离,我们可以实现任务的并⾏处理,从
⽽提⾼程序的执⾏效率 -
并发控制:在多线程编程中,我们可能需要等待某些任务完成后才能继续执⾏其他操作。通过使⽤
std::future,我们可以实现线程之间的同步,确保任务完成后再获取结果并继续执⾏后续操作 -
结果获取:std::future提供了⼀种安全的⽅式来获取异步任务的结果。我们可以使⽤
std::future::get()函数来获取任务的结果,此函数会阻塞当前线程,直到异步操作完成。这样,在
调⽤get()函数时,我们可以确保已经获取到了所需的结果
下面来看官方文档对于这个类的讲述:

大体意思是,future会作为一个返回值来进行接收,可以通过下面的三种方式来进行调用,这里展示第一种:async
async

在这个函数当中,就是一个经典的调用异步操作来执行的操作,对于函数参数来说,Fn表示的是这是一个要执行的函数,后面的args表示的是这个函数的参数,而对于这个函数来说,它存在一种函数的重载,这个函数的重载可以在最前面加上一个调用的策略,可以使得是立刻进行执行和获取函数的返回值,或者是在调用get函数再进行函数返回值的获取,下面使用一个实例代码来进行演示
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 <chrono>
#include <iostream>
#include <future>
#include <thread>
using namespace std;// 模拟一个加法的环境
int add(int num1, int num2)
{cout << "加法" << endl;return num1 + num2;
}void deferred_solve()
{cout << "deferred" << endl;cout << "------1------" << endl;future<int> fut = async(launch::deferred, add, 10, 20);cout << "------2------" << endl;this_thread::sleep_for(chrono::seconds(1));cout << "------3------" << endl;int res = fut.get();cout << "------4------" << endl;cout << "运行结果" << res << endl;
}void async_solve()
{cout << "async" << endl;cout << "------1------" << endl;future<int> fut = async(launch::async, add, 10, 20);cout << "------2------" << endl;this_thread::sleep_for(chrono::seconds(1));cout << "------3------" << endl;int res = fut.get();cout << "------4------" << endl;cout << "运行结果" << res << endl;
}int main()
{cout << "deferred: " << endl;deferred_solve();cout << endl;cout << "async: " << endl;async_solve();cout << endl;return 0;
}
运行结果如下所示:
deferred:
deferred
------1------
------2------
------3------
加法
------4------
运行结果30async:
async
------1------
------2------
加法
------3------
------4------
运行结果30
从上可以看出一些端倪,对于deferred这种策略来说,它的策略是在进行get方法的时候再进行资源的计算,而对于async这样的策略来说,更多的是在进行调用之后就会进行计算,在这种调用之后,会立刻再开一个工作线程把内容计算完毕后传递回主函数,这是两个基本的调用逻辑。
promise
std::promise提供了⼀种设置值的⽅式,它可以在设置之后通过相关联的std::future对象进⾏读取。换
种说法就是之前说过std::future可以读取⼀个异步函数的返回值了,但是要等待就绪,⽽std::promise就提供⼀种⽅式⼿动让std::future就绪
#include <iostream>
#include <thread>
#include <future>using namespace std;void add(int num1, int num2, promise<int>& pro)
{pro.set_value(num1 + num2);return;
}int main()
{promise<int> pro;future<int> fut = pro.get_future();thread th(add, 10, 20, ref(pro));int res = fut.get();cout << "执行结果: " << res << endl;th.join();return 0;
}
这个场景本质上就是利用了一个promise对象来和future对象建立了关系,如果在获取future对象的时候并没有发生值改变,就会阻塞等待,保证了异步的基本进行
package task
对于这种调用的方式,可以把它生成的对象当成是一个可调用对象,下面演示其基本用法
#include <iostream>
#include <thread>
#include <future>
#include <memory>
using namespace std;int add(int num1, int num2)
{return num1 + num2;
}int main()
{auto ptask = make_shared<packaged_task<int(int, int)>>(add);future<int> fut = ptask->get_future();thread th([ptask](){(*ptask)(10, 20);});int sum = fut.get();cout << sum << endl;th.join();return 0;
}
C++11线程池实现
基于线程池执⾏任务的时候,⼊⼝函数内部执⾏逻辑是固定的,因此选择std::packaged_task加上std::future的组合来实现。
线程池的⼯作思想:
- ⽤⼾传⼊要执⾏的函数,以及需要处理的数据(函数的参数),由线程池中的⼯作线程来执⾏函数完成任务
实现:
- 管理的成员
- 任务池:⽤vector维护的⼀个函数任务池⼦
- 互斥锁&条件变量:实现同步互斥
- ⼀定数量的⼯作线程:⽤于不断从任务池取出任务执⾏任务
- 结束运⾏标志:以便于控制线程池的结束。
- 管理的操作:
- ⼊队任务:⼊队⼀个函数和参数
- 停⽌运⾏:终⽌线程池
#include <features.h>
#include <iostream>
#include <functional>
#include <memory>
#include <thread>
#include <future>
#include <mutex>
#include <condition_variable>
#include <vector>
using namespace std;class threadpool
{using Functor = function<void(void)>;
public:threadpool(int count = 1) : _stop(false){for(int i = 0; i < 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函数的内部,会把这些传入的函数和参数封装为一个packaged_task// 然后使用lambda表达式生成一个可调用对象,放到任务池中,让工作线程取出执行template<typename F, typename ...Args>auto push(const F&& func, Args&& ...args) -> future<decltype(func(args...))>{// 1. 把传入的函数封装为一个packaged任务// 把返回类型获取出来using return_type = decltype(func(args...));// 把函数对象和函数参数绑定到一起auto tmp_func = bind(forward<F>(func), forward<Args>(args)...);// 把整体的tmp_func绑定成一个任务auto task = make_shared<packaged_task<return_type()>>(tmp_func);future<return_type> fut = task->get_future();// 2. 构造一个lambda匿名函数,函数内执行任务对象{unique_lock<mutex> lock(_mutex);// 3. 把匿名函数对象放到任务池中_taskpool.push_back([task](){ (*task)(); });_cv.notify_one();}return fut;}
private:void entry(){while(!_stop){vector<Functor> tmp_taskpool;{unique_lock<mutex> lock(_mutex);_cv.wait(lock, [this](){ return _stop || !_taskpool.empty(); });tmp_taskpool.swap(_taskpool);}for(auto& task : tmp_taskpool)task();}}
private:atomic<bool> _stop;vector<Functor> _taskpool;mutex _mutex;condition_variable _cv;vector<thread> _threads;
};
相关文章:
异步操作实现线程池
文章目录 futureasyncpromisepackage task C11线程池实现 future 在C11标准库中,提供了一个future的模板类,它表示的是一个异步操作的结果,当在多线程编程中使用异步任务的时候,使用这个类可以帮助在需要的时候获取到对应的数据处…...
长期提供APX515/B原装二手APX525/B音频分析仪
Audio Precision APx515 是一款针对生产测试而优化的高性能音频分析仪。它因其速度、性能、自动化和易用性而成为一流的仪器。它具有卓越的性能,具有 –106 dB 的典型 THDN、1M 点 FFT 和 192k 数字 I/O,以及所有 APx 系列音频分析仪的一键式自动化和易用…...
【数据库差异研究】update与delete使用表别名的研究
目录 ⚛️总结 ☪️1 Update ♋1.1 测试用例UPDATE users as a SET a.age 111 WHERE a.name Alice; ♏1.2 测试用例UPDATE users as a SET a.age 111 WHERE name Alice; ♐1.3 测试用例UPDATE users as a SET age 111 WHERE a.name Alice; ♑1.4 测试用例UPDATE us…...
idea远程连接docker
idea远程连接docker docker、ubuntu、linux、远程连接、IntelliJ idea注意!本文中开启docker远程连接的方法只能在确定环境安全的内网中使用,不可在公网服务器设置,有极大安全风险! 注意!本文中开启docker远程连接的…...
Docker 安装 ClickHouse 教程
Docker 安装 ClickHouse 教程 创建目录 首先,创建必要的目录用于存放 ClickHouse 的配置、数据和日志文件。 mkdir -p /home/clickhouse/conf mkdir -p /home/clickhouse/data mkdir -p /home/clickhouse/log chmod -R 777 /home/clickhouse/conf chmod -R 777 /…...
过渡到内存安全语言:挑战和注意事项
开放源代码安全基金会 ( OpenSSF )总经理 Omkhar Arasaratnam 讨论了内存安全编程语言的演变及其为应对 C 和 C 等语言的局限性而出现的现象。 内存安全问题已存在五十多年,它要求程序员从内存管理任务中抽离出来。 Java、Rust、Python 和 JavaScript 等现代语言通…...
在Pycharm中安装Cv2
安装OpenCV: 在Terminal中,输入以下pip命令来安装OpenCV: pip install opencv-python pip install opencv-contrib-python 如果下载速度较慢,可以考虑使用国内的pip镜像源,如清华大学源: pip install openc…...
减少重复的请求之promise缓存池(构造器版) —— 缓存promise,多次promise等待并返回第一个promise的结果
减少重复的请求之promise缓存池 —— 缓存promise,多次promise等待并返回第一个promise的结果 背景简介 当一个业务组件初始化调用了接口,统一个页面多吃使用同一个组件,将会请求大量重复的接口 如果将promise当作一个普通的对象࿰…...
cdq+bitset处理高维偏序
高维偏序 CDQ分治 假设处理的区间为 [ l , r ] [l,r] [l,r] ,CDQ分治的过程: 如果 l ≥ r l\geq r l≥r ,返回。设区间中点为 m i d mid mid ,递归处理 [ l , m i d ] [l,mid] [l,mid] 和 [ m i d 1 , r ] [mid1,r] [mid…...
敏捷开发和传统开发,你更适合哪种?
时间:2024年 10月 03日 作者:小蒋聊技术 邮箱:wei_wei10163.com 微信:wei_wei10 音频:喜马拉雅 大家好,欢迎来到“小蒋聊技术”,我是小蒋!今天我们来聊聊两个开发模式的“对决”…...
python之with
with上下文管理是什么呢? 一般都是使用系统提供的一些with语句,列如我要去读取一些数据进行分析,就可以使用with open去读取某些数据,或者我要把一些图片给他保存到某些地方,可以用with给他写入。 上下午管理器with是…...
vue3 升级实战笔记
最近要将公司项目的移动端进行 vue3 的升级工作,就顺便记录下升级过程。 项目迁移的思路 我的想法是最小改动原则。 从 vue2.x 升级到 vue3,且使用 vue3 的 选项式 API。构建工具要从 vue-cli(webpack)升级到 vite。路由需要升级到…...
利用函数模块化代码实操 ← Python
【知识点】 ● 模块化可以使代码易于维护和调试,并且提高代码的重用性。 ● 函数可以用来减少冗余的代码并提高代码的可重用性。函数也可以用来模块化代码并提高程序的质量。 ● 在 Python 中,可以将函数的定义放在一个被称为模块的文件中,这种文件的后缀…...
Java高效编程(12):重写toString方法
解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 尽管 Object 类提供了 toString 方法的默认实现,但它返回的字符串通常不是类的使用者想要看到的。默认返回的字符串格式是类名加上“”符号和哈希码的十六进制表示,例如 PhoneNu…...
谷歌给到的185个使用生成式AI的案例
很多公司从利用AI回答问题,进而使用AI进行预测,向使用生成式AI Agent转变。AI Agent的独特之处在于它们可以采取行动以实现特定目标,比如引导购物者找到合适的鞋子,帮助员工寻找合适的健康福利,或在护理人员交接班期间…...
程序员如何通过专业与软技能提升核心竞争力
一、引言 随着AIGC的兴起,AI辅助编程工具如chatgpt、midjourney、claude等接二连三地涌现,编程领域的变革正逐步深化。面对这一变革,程序员们对于未来工作的前景有着种种不同的担忧和期待。他们担心AI可能取代部分编程工作,同时…...
基于YOLOv8的智能植物监测机器人
摘要:针对传统的植物病害检测方法依赖专家的经验,耗时耗力,并且准确性受限于个人的水平等问题。文中提出无线通信模块采用HTTP协议来传输数据图片,采用SoC核心处理器实现了便携化,采用对射式红外避障传感器实现自动避障功能。以YOLOv8算法为控制核心,并添加注意力机制以提…...
2024年OpenAI DevDay发布实时 API、提示缓存等新功能
就在几天前,一些重要人物如前 CTO Mira Murati 离开了 OpenAI。因此,看到 Sam Altman 在 DevDay 上登台,讨论开发者的新产品,感觉有点奇怪。 随着公司内部的这些变化,你不禁会想:我们还应该信任他吗&#…...
Raspberry Pi3B+之安装bookworm+Rpanion系统
Raspberry Pi3B之安装bookwormRpanion系统 1. 源由2. 系统安装3. 系统安装3.1 烧录系统3.2 设备接线3.3 配置无线3.4 更新系统3.5 安装git3.6 克隆Rpanion3.7 安装Rpanion 4. 系统管理5. 附录问题1:error: externally-managed-environment问题2:bookworm…...
无人机专业除理论外,飞手执照、组装、调试实操技术详解
无人机专业的学习除了丰富的理论知识外,飞手执照的获取、无人机的组装与调试等实操技术也是至关重要的。以下是对这些方面的详细解析: 一、无人机飞手执照 1. 必要性 法规要求:根据《民用无人驾驶航空器系统驾驶员管理暂行规定》等相关法规…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...
基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
