【C/C++】C++并发编程:std::async与std::thread深度对比
文章目录
- C++并发编程:std::async与std::thread深度对比
- 1 核心设计目的以及区别
- 2 详细对比分析
- 3 代码对比示例
- 4 适用场景建议
- 5 总结
C++并发编程:std::async与std::thread深度对比
在 C++ 中,std::async
和 std::thread
都是用于并发编程的工具,但它们在实现方式、资源管理和适用场景上有显著区别。
1 核心设计目的以及区别
特性 | std::async | std::thread |
---|---|---|
目标 | 简化异步任务的启动和结果获取 | 提供底层线程的直接控制 |
抽象层级 | 高级抽象(封装线程和结果管理) | 低级抽象(直接操作线程) |
典型场景 | 需要异步执行并获取结果的短期任务 | 需要精细控制线程生命周期的复杂任务 |
特性 | std::thread | std::async |
---|---|---|
线程创建 | 直接创建新线程 | 可能创建新线程,也可能延迟执行(取决于策略) |
返回值 | 无返回值,需通过共享变量传递结果 | 返回 std::future ,可异步获取结果 |
资源管理 | 需手动管理线程生命周期(join() /detach() ) | 自动管理任务生命周期,future 析构时自动处理 |
异常处理 | 线程内未捕获的异常会导致程序崩溃 | 异常会被捕获并存储在 future 中 |
执行策略 | 总是立即执行 | 可指定 std::launch::async (立即执行)或 std::launch::deferred (延迟执行) |
适用场景 | 需要精细控制线程行为(如优先级、同步) | 需要异步执行并获取结果,或延迟执行任务 |
特性 | std::async | std::thread |
---|---|---|
返回值传递 | 通过 std::future 自动获取结果 | 需手动传递(如 std::promise 或全局变量) |
异常处理 | 异常通过 future.get() 自动传递到调用线程 | 线程内未捕获的异常会导致 std::terminate |
示例 | auto f = std::async(func); try { f.get(); } catch(...) {} | std::promise<int> p; auto f = p.get_future(); std::thread t([&p]{ try { p.set_value(func()); } catch(...) { p.set_exception(...); } }); |
特性 | std::async | std::thread |
---|---|---|
线程生命周期 | std::future 析构时自动等待线程完成(若策略为 async ) | 必须显式调用 join() 或 detach() ,否则程序终止 |
资源泄漏风险 | 低(自动管理) | 高(需手动管理) |
示例 | cpp { auto f = std::async(func); } // 自动等待 | cpp std::thread t(func); t.join(); // 必须显式调用 |
特性 | std::async | std::thread |
---|---|---|
线程池支持 | 可能使用线程池(依赖编译器实现) | 每次创建新线程 |
适用场景 | 短期任务(避免频繁创建线程的开销) | 长期任务或需要独占线程的场景 |
性能风险 | 若默认策略非异步,可能意外延迟执行 | 频繁创建线程可能导致资源耗尽 |
2 详细对比分析
- 线程创建与执行
-
std::thread
-
强制创建新线程:无论系统资源是否充足,都会立即启动新线程执行任务。
-
资源风险:若线程数量过多(如超过系统限制),可能导致程序崩溃。
-
示例:
std::thread t([](){ /* 任务代码 */ }); t.join(); // 必须手动等待线程结束
-
-
std::async
-
策略控制:
◦std::launch::async
:强制创建新线程执行任务。
◦std::launch::deferred
:延迟执行,仅在调用get()
或wait()
时执行(不创建新线程)。
◦ 默认策略(async | deferred
):由系统决定是否创建线程。 -
资源优化:可能复用线程池中的线程,减少创建开销。
-
示例:
auto fut = std::async(std::launch::async, [](){ return 42; }); int result = fut.get(); // 阻塞等待结果
-
- 返回值与结果获取
-
std::thread
- 无法直接获取返回值,需通过共享变量或回调函数传递结果。
- 示例:
int result = 0; std::thread t([&result](){ result = 42; }); t.join();
-
std::async
-
通过
std::future
自动获取返回值,支持异步等待。 -
示例:
auto fut = std::async([](){ return 42; }); int result = fut.get(); // 阻塞获取结果
-
- 异常处理
-
std::thread
-
线程内抛出的异常若未被捕获,会导致程序终止。
-
示例:
std::thread t([](){ throw std::runtime_error("error"); }); t.join(); // 程序崩溃
-
-
std::async
-
异常会被捕获并存储在
std::future
中,调用get()
时重新抛出。 -
示例:
auto fut = std::async([](){ throw std::runtime_error("error"); }); try { fut.get(); } catch (const std::exception& e) { /* 处理异常 */ }
-
- 性能与资源消耗
-
std::thread
- 高开销:线程创建和销毁涉及操作系统调度,频繁使用可能导致性能瓶颈。
- 适用场景:需要长期运行的独立任务(如后台服务线程)。
-
std::async
- 低开销:可能复用线程池中的线程,减少创建/销毁成本。
- 适用场景:短时任务或需要灵活调度的工作(如并行计算、I/O 密集型操作)。
3 代码对比示例
任务:并行计算并返回结果
-
使用
std::thread
:#include <iostream> #include <thread> #include <future>int compute() {return 42; }int main() {std::thread t(compute);// 无法直接获取结果,需通过共享变量t.join();return 0; }
-
使用
std::async
:#include <iostream> #include <future>int compute() {return 42; }int main() {auto fut = std::async(compute);int result = fut.get(); // 直接获取结果std::cout << "Result: " << result << std::endl;return 0; }
4 适用场景建议
场景 | 推荐工具 | 原因 |
---|---|---|
需要获取异步任务结果 | std::async | 通过 std::future 简化结果传递,避免共享变量竞争 |
需要控制线程优先级或调度策略 | std::thread | 提供底层线程控制能力 |
短时任务或高并发场景 | std::async | 可能复用线程池,减少资源开销 |
长时间运行的后台服务线程 | std::thread | 需要保持线程活跃状态 |
-
优先
std::async
的场景:- 需要异步执行并获取结果。
- 关注异常安全和代码简洁性。
- 短期任务,避免手动管理线程。
-
优先
std::thread
的场景:- 需要精确控制线程生命周期(如分离线程、自定义调度)。
- 长期运行的后台任务(如服务线程)。
- 需要跨线程共享复杂状态或资源。
5 总结
std::thread
:适合需要直接控制线程行为、长期运行的任务,但需手动管理生命周期和结果传递。std::async
:适合需要异步执行并获取结果、或希望系统自动优化资源使用的场景,但对执行策略需谨慎选择。std::async
的隐藏阻塞:std::future
析构时会隐式等待任务完成,可能导致意外阻塞。- 线程局部存储(TLS):
std::async
的线程可能复用,导致 TLS 状态残留。 - 编译器差异:
std::async
的线程池行为(如线程复用策略)可能因编译器实现不同而不同。
维度 | std::async | std::thread |
---|---|---|
结果获取 | 自动通过 future | 需手动使用 promise 或共享变量 |
异常传播 | 自动传递异常 | 需手动捕获并处理 |
线程管理 | 自动等待线程完成 | 需显式调用 join() /detach() |
灵活性 | 适合简单任务 | 适合需要精细控制的场景 |
性能优化 | 可能复用线程(依赖实现) | 直接控制线程创建和销毁 |
关键原则:
- 若需结果或异常安全,优先选择
std::async
。 - 若需精细控制线程行为(如优先级、同步),使用
std::thread
。
相关文章:
【C/C++】C++并发编程:std::async与std::thread深度对比
文章目录 C并发编程:std::async与std::thread深度对比1 核心设计目的以及区别2 详细对比分析3 代码对比示例4 适用场景建议5 总结 C并发编程:std::async与std::thread深度对比 在 C 中,std::async 和 std::thread 都是用于并发编程的工具&am…...
每日算法刷题Day11 5.20:leetcode不定长滑动窗口求最长/最大6道题,结束不定长滑动窗口求最长/最大,用时1h20min
6. 1695.删除子数组的最大得分(中等) 1695. 删除子数组的最大得分 - 力扣(LeetCode) 思想 1.给你一个正整数数组 nums ,请你从中删除一个含有 若干不同元素 的子数组**。**删除子数组的 得分 就是子数组各元素之 和 。 返回 只删除一个 子…...
STL中的Vector(顺序表)
vector容器的基本用法: template<class T> class vector { T* _a; size_t size; size_t capacity; } 尾插和遍历: vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3);//遍历 for(int i0;i<v.size();i) {cout<<…...
iOS Runtime与RunLoop的对比和使用
Runtime 机制 核心概念 Objective-C 的动态特性:Objective-C 是一门动态语言,很多工作都是在运行时而非编译时决定的消息传递机制:方法调用实际上是发送消息 objc_msgSend(receiver, selector, ...)方法决议机制:动态方法解析、…...

解决vscode在任务栏显示白色图标
长久不用,不知道怎么着就显示成白色图标,虽然不影响使用,但是看起来不爽 问了豆包,给了个解决方法: 1、打开隐藏文件, 由于图标缓存文件是隐藏文件,首先点击资源管理器中的 “查看” 菜单&am…...

架构思维:构建高并发扣减服务_分布式无主架构
文章目录 Pre无主架构的任务简单实现分布式无主架构 设计和实现扣减中的返还什么是扣减的返还返还实现原则原则一:扣减完成才能返还原则二:一次扣减可以多次返还原则三:返还的总数量要小于等于原始扣减的数量原则四:返还要保证幂等…...
Vue 3 官方 Hooks 的用法与实现原理
Vue 3 引入了 Composition API,使得生命周期钩子(hooks)在函数式风格中更清晰地表达。本篇文章将从官方 hooks 的使用、实现原理以及自定义 hooks 的结构化思路出发,全面理解 Vue 3 的 hooks 系统。 📘 1. Vue 3 官方生…...
Vue3 打印表格、Element Plus 打印、前端打印、表格导出打印、打印插件封装、JavaScript 打印、打印预览
🚀 Vue3 高级表格打印工具封装(支持预览、分页、样式美化) 现已更新至npm # npm npm install vue-table-print# yarn yarn add vue-table-print# pnpm pnpm add vue-table-printgithunb地址: https://github.com/zhoulongshao/vue-table-print/blob/main/README.MD关键词…...
湖北理元理律师事务所:专业债务优化如何助力负债者重获生活掌控权
在当前经济环境下,个人债务问题日益凸显。湖北理元理律师事务所通过其专业的债务优化服务,为负债群体提供了一条合法合规的解决路径。本文将客观分析专业债务规划的实际价值,不涉及任何营销内容。 一、债务优化的核心价值 科学评估…...
RAGFlow知识检索原理解析:混合检索架构与工程实践
一、核心架构设计 RAGFlow构建了四阶段处理流水线,其检索系统采用双路召回+重排序的混合架构: S c o r e f i n a l = α ⋅ B M...
5月22总结
P1024 [NOIP 2001 提高组] 一元三次方程求解 题目描述 有形如:$ a x^3 b x^2 c x d 0 $ 这样的一个一元三次方程。给出该方程中各项的系数($ a,b,c,d $ 均为实数),并约定该方程存在三个不同实根(根的范围在 $ -1…...
Java设计模式之桥接模式:从入门到精通
1. 桥接模式概述 1.1 定义与核心思想 桥接模式(Bridge Pattern)是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立变化。这种模式通过提供桥梁结构(Bridge)将抽象和实现解耦。 专业定义:桥接模式将抽象部分与它的实现部分分离,使它们都可以独立地变化…...

uni-app学习笔记九-vue3 v-for指令
v-for 指令基于一个数组来渲染一个列表。v-for 指令的值需要使用 item in items 形式的特殊语法,其中 items 是源数据的数组,而 item 是迭代项的别名: <template><view v-for"(item,index) in 10" :key"index"…...

MAC电脑中右键后复制和拷贝的区别
在Mac电脑中,右键菜单中的“复制”和“拷贝”操作在功能上有所不同: 复制 功能:在选定的位置创建一个与原始文件相同的副本。快捷键:CommandD用于在当前位置快速复制文件,CommandC用于将内容复制到剪贴板。效果&…...
Regmap子系统之六轴传感器驱动-编写icm20607.c驱动
(一)在驱动中要操作很多芯片相关的寄存器,所以需要先新建一个icm20607.h的头文件,用来定义相关寄存器值。 #ifndef ICM20607_H #define ICM20607_H /*************************************************************** 文件名 : i…...
常见高危端口解析:网络安全中的“危险入口”
目录 1. 经典高危端口列表 2. 典型漏洞案例:445端口与永恒之蓝 攻击原理 防御方案 Linux命令 2. 防护策略建议 三、扩展思考:从端口到攻防体系 结语 1. 经典高危端口列表 端口号 协议/服务 风险场景 21 FTP 明文传输凭据、弱密码爆破、匿名…...

华为2025年校招笔试手撕真题教程(二)
一、题目 大湾区某城市地铁线路非常密集,乘客很难一眼看出选择哪条线路乘坐比较合适,为了解决这个问题,地铁公司希望你开发一个程序帮助乘客挑选合适的乘坐线路,使得乘坐时间最短,地铁公司可以提供的数据是各相邻站点…...

征程 6 J6E/M linear 双int16量化支持替代方案
1.背景简介 当发现使用 plugin 精度 debug 工具定位到是某个 linear 敏感时,示例如下: op_name sensitive_type op_type L1 quant_dty…...

深度学习模块缝合拼接方法套路+即插即用模块分享
前言 在深度学习中,模型的设计往往不是从头开始,而是通过组合不同的模块来构建。这种“模块缝合”技术,就像搭积木一样,把不同的功能模块拼在一起,形成一个强大的模型。今天,我们就来聊聊四种常见的模块缝…...

改写视频生产流程!快手SketchVideo开源:通过线稿精准控制动态分镜的AI视频生成方案
Sketch Video 的核心特点 Sketch Video 通过手绘生成动画的形式,将复杂的信息以简洁、有趣的方式展现出来。其核心特点包括: 超强吸引力 Sketch Video 的手绘风格赋予了视频一种质朴而真实的质感,与常见的精致特效视频形成鲜明对比。这种独…...
Graphics——基于.NET 的 CAD 图形预览技术研究与实现——CAD c#二次开发
一、Graphics 类的本质与作用 Graphics 是 .NET 框架中 System.Drawing 命名空间下的核心类,用于在二维画布(如 Bitmap 图像)上绘制图形、文本或图像。它相当于 “绘图工具”,提供了一系列方法(如 DrawLine、FillElli…...
ElasticSearch 8.x 快速上手并了解核心概念
目录 核心概念概念总结 常见操作索引的常见操作常见的数据类型指定索引库字段类型mapping查看索引库的字段类型最高频使用的数据类型 核心概念 在新版Elasticsearch中,文档document就是一行记录(json),而这些记录存在于索引库(index)中, 索引名称必须是…...
AI神经网络降噪 vs 传统单/双麦克风降噪的核心优势对比
1. 降噪原理的本质差异 对比维度传统单/双麦克风降噪AI神经网络降噪技术基础基于固定规则的信号处理(如谱减法、维纳滤波)基于深度学习的动态建模(DNN/CNN/Transformer)噪声样本依赖预设有限噪声类型训练数据覆盖数十万种真实环境…...

04-Web后端基础(基础知识)
而像HTML、CSS、JS 以及图片、音频、视频等这些资源,我们都称为静态资源。 所谓静态资源,就是指在服务器上存储的不会改变的数据,通常不会根据用户的请求而变化。 那与静态资源对应的还有一类资源,就是动态资源。那所谓动态资源&…...

Spring Cloud生态与技术选型指南:如何构建高可用的微服务系统?
引言:为什么选择Spring Cloud? 作为全球开发者首选的微服务框架,Spring Cloud凭借其开箱即用的组件、与Spring Boot的无缝集成,以及活跃的社区生态,成为企业级微服务架构的基石。但在实际项目中,如何从众多…...

手写简单的tomcat
首先,Tomcat是一个软件,所有的项目都能在Tomcat上加载运行,Tomcat最核心的就是Servlet集合,本身就是HashMap。Tomcat需要支持Servlet,所以有servlet底层的资源:HttpServlet抽象类、HttpRequest和HttpRespon…...

高等数学-积分
一、不定积分 定理:如果函数f(x)在区间I上连续,那么f(x)在区间I上一定有原函数,即一定存在区间I上的可导函数F(x),使得F(x)f(x) ,x∈I 简单地说:连续函数必有原函数。 极限lim*0->x {[∫*0^x sin(t^2)…...

IOS平台Unity3D AOT全局模块结构分析
分析背景 由于IOS平台中不允许执行动态代码,Unity 4.6之前的版本在IOS平台中采用了AOT的处理方式,提前将C#代码静态编译为机器识别的二进制机器码。Unity引擎4.6之前的版本中IOS框架采用了Mono的AOT机制实现静态编译和处理,本文针对全局AOT模…...
Vue 3.0中自定义指令
自定义指令是增强 Vue 组件的重要手段。常见的内置指令有: v-if、v-show、v-model、v-bind、v-on等。 本文将详细讲解如何创建和使用自定义指令,关注以下几个关键点: 1. 指令的钩子函数:类似于生命周期钩子函数。 2. 指令钩子函…...
在 语义分割 和 图像分类 任务中,image、label 和 output 的形状会有所不同。
1. 图像分类 (Image Classification) 图像分类 任务是将整个图像分类为一个类别。通常,output 是对整个图像的类别的预测,而 label 是该图像的真实类别。 1.1 image 的形状 image 是输入图像数据,通常是一个四维张量: 形状&…...