当前位置: 首页 > news >正文

异步操作实现线程池

文章目录

  • 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的组合来实现。
线程池的⼯作思想:

  • ⽤⼾传⼊要执⾏的函数,以及需要处理的数据(函数的参数),由线程池中的⼯作线程来执⾏函数完成任务

实现:

  1. 管理的成员
  • 任务池:⽤vector维护的⼀个函数任务池⼦
  • 互斥锁&条件变量:实现同步互斥
  • ⼀定数量的⼯作线程:⽤于不断从任务池取出任务执⾏任务
  • 结束运⾏标志:以便于控制线程池的结束。
  1. 管理的操作:
  • ⼊队任务:⼊队⼀个函数和参数
  • 停⽌运⾏:终⽌线程池
#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标准库中&#xff0c;提供了一个future的模板类&#xff0c;它表示的是一个异步操作的结果&#xff0c;当在多线程编程中使用异步任务的时候&#xff0c;使用这个类可以帮助在需要的时候获取到对应的数据处…...

长期提供APX515/B原装二手APX525/B音频分析仪

Audio Precision APx515 是一款针对生产测试而优化的高性能音频分析仪。它因其速度、性能、自动化和易用性而成为一流的仪器。它具有卓越的性能&#xff0c;具有 –106 dB 的典型 THDN、1M 点 FFT 和 192k 数字 I/O&#xff0c;以及所有 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注意&#xff01;本文中开启docker远程连接的方法只能在确定环境安全的内网中使用&#xff0c;不可在公网服务器设置&#xff0c;有极大安全风险&#xff01; 注意&#xff01;本文中开启docker远程连接的…...

Docker 安装 ClickHouse 教程

Docker 安装 ClickHouse 教程 创建目录 首先&#xff0c;创建必要的目录用于存放 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 等语言的局限性而出现的现象。 内存安全问题已存在五十多年&#xff0c;它要求程序员从内存管理任务中抽离出来。 Java、Rust、Python 和 JavaScript 等现代语言通…...

在Pycharm中安装Cv2

安装OpenCV&#xff1a; 在Terminal中&#xff0c;输入以下pip命令来安装OpenCV&#xff1a; pip install opencv-python pip install opencv-contrib-python 如果下载速度较慢&#xff0c;可以考虑使用国内的pip镜像源&#xff0c;如清华大学源&#xff1a; pip install openc…...

减少重复的请求之promise缓存池(构造器版) —— 缓存promise,多次promise等待并返回第一个promise的结果

减少重复的请求之promise缓存池 —— 缓存promise&#xff0c;多次promise等待并返回第一个promise的结果 背景简介 当一个业务组件初始化调用了接口&#xff0c;统一个页面多吃使用同一个组件&#xff0c;将会请求大量重复的接口 如果将promise当作一个普通的对象&#xff0…...

cdq+bitset处理高维偏序

高维偏序 CDQ分治 假设处理的区间为 [ l , r ] [l,r] [l,r] &#xff0c;CDQ分治的过程&#xff1a; 如果 l ≥ r l\geq r l≥r &#xff0c;返回。设区间中点为 m i d mid mid &#xff0c;递归处理 [ l , m i d ] [l,mid] [l,mid] 和 [ m i d 1 , r ] [mid1,r] [mid…...

敏捷开发和传统开发,你更适合哪种?

时间&#xff1a;2024年 10月 03日 作者&#xff1a;小蒋聊技术 邮箱&#xff1a;wei_wei10163.com 微信&#xff1a;wei_wei10 音频&#xff1a;喜马拉雅 大家好&#xff0c;欢迎来到“小蒋聊技术”&#xff0c;我是小蒋&#xff01;今天我们来聊聊两个开发模式的“对决”…...

python之with

with上下文管理是什么呢&#xff1f; 一般都是使用系统提供的一些with语句&#xff0c;列如我要去读取一些数据进行分析&#xff0c;就可以使用with open去读取某些数据&#xff0c;或者我要把一些图片给他保存到某些地方&#xff0c;可以用with给他写入。 上下午管理器with是…...

vue3 升级实战笔记

最近要将公司项目的移动端进行 vue3 的升级工作&#xff0c;就顺便记录下升级过程。 项目迁移的思路 我的想法是最小改动原则。 从 vue2.x 升级到 vue3&#xff0c;且使用 vue3 的 选项式 API。构建工具要从 vue-cli&#xff08;webpack&#xff09;升级到 vite。路由需要升级到…...

利用函数模块化代码实操 ← Python

【知识点】 ● 模块化可以使代码易于维护和调试&#xff0c;并且提高代码的重用性。 ● 函数可以用来减少冗余的代码并提高代码的可重用性。函数也可以用来模块化代码并提高程序的质量。 ● 在 Python 中&#xff0c;可以将函数的定义放在一个被称为模块的文件中,这种文件的后缀…...

Java高效编程(12):重写toString方法

解锁Python编程的无限可能&#xff1a;《奇妙的Python》带你漫游代码世界 尽管 Object 类提供了 toString 方法的默认实现&#xff0c;但它返回的字符串通常不是类的使用者想要看到的。默认返回的字符串格式是类名加上“”符号和哈希码的十六进制表示&#xff0c;例如 PhoneNu…...

谷歌给到的185个使用生成式AI的案例

很多公司从利用AI回答问题&#xff0c;进而使用AI进行预测&#xff0c;向使用生成式AI Agent转变。AI Agent的独特之处在于它们可以采取行动以实现特定目标&#xff0c;比如引导购物者找到合适的鞋子&#xff0c;帮助员工寻找合适的健康福利&#xff0c;或在护理人员交接班期间…...

程序员如何通过专业与软技能提升核心竞争力

一、引言  随着AIGC的兴起&#xff0c;AI辅助编程工具如chatgpt、midjourney、claude等接二连三地涌现&#xff0c;编程领域的变革正逐步深化。面对这一变革&#xff0c;程序员们对于未来工作的前景有着种种不同的担忧和期待。他们担心AI可能取代部分编程工作&#xff0c;同时…...

基于YOLOv8的智能植物监测机器人

摘要:针对传统的植物病害检测方法依赖专家的经验,耗时耗力,并且准确性受限于个人的水平等问题。文中提出无线通信模块采用HTTP协议来传输数据图片,采用SoC核心处理器实现了便携化,采用对射式红外避障传感器实现自动避障功能。以YOLOv8算法为控制核心,并添加注意力机制以提…...

2024年OpenAI DevDay发布实时 API、提示缓存等新功能

就在几天前&#xff0c;一些重要人物如前 CTO Mira Murati 离开了 OpenAI。因此&#xff0c;看到 Sam Altman 在 DevDay 上登台&#xff0c;讨论开发者的新产品&#xff0c;感觉有点奇怪。 随着公司内部的这些变化&#xff0c;你不禁会想&#xff1a;我们还应该信任他吗&#…...

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&#xff1a;error: externally-managed-environment问题2&#xff1a;bookworm…...

无人机专业除理论外,飞手执照、组装、调试实操技术详解

无人机专业的学习除了丰富的理论知识外&#xff0c;飞手执照的获取、无人机的组装与调试等实操技术也是至关重要的。以下是对这些方面的详细解析&#xff1a; 一、无人机飞手执照 1. 必要性 法规要求&#xff1a;根据《民用无人驾驶航空器系统驾驶员管理暂行规定》等相关法规…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...