【C/C++】C++并发编程:std::async与std::thread深度对比

news2025/5/23 14:38:15

文章目录

  • C++并发编程:std::async与std::thread深度对比
    • 1 核心设计目的以及区别
    • 2 详细对比分析
    • 3 代码对比示例
    • 4 适用场景建议
    • 5 总结

C++并发编程:std::async与std::thread深度对比

在 C++ 中,std::asyncstd::thread 都是用于并发编程的工具,但它们在实现方式、资源管理和适用场景上有显著区别。


1 核心设计目的以及区别

特性std::asyncstd::thread
目标简化异步任务的启动和结果获取提供底层线程的直接控制
抽象层级高级抽象(封装线程和结果管理)低级抽象(直接操作线程)
典型场景需要异步执行并获取结果的短期任务需要精细控制线程生命周期的复杂任务
特性std::threadstd::async
线程创建直接创建新线程可能创建新线程,也可能延迟执行(取决于策略)
返回值无返回值,需通过共享变量传递结果返回 std::future,可异步获取结果
资源管理需手动管理线程生命周期(join()/detach()自动管理任务生命周期,future 析构时自动处理
异常处理线程内未捕获的异常会导致程序崩溃异常会被捕获并存储在 future
执行策略总是立即执行可指定 std::launch::async(立即执行)或 std::launch::deferred(延迟执行)
适用场景需要精细控制线程行为(如优先级、同步)需要异步执行并获取结果,或延迟执行任务
特性std::asyncstd::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::asyncstd::thread
线程生命周期std::future 析构时自动等待线程完成(若策略为 async必须显式调用 join()detach(),否则程序终止
资源泄漏风险低(自动管理)高(需手动管理)
示例cpp { auto f = std::async(func); } // 自动等待 cpp std::thread t(func); t.join(); // 必须显式调用
特性std::asyncstd::thread
线程池支持可能使用线程池(依赖编译器实现)每次创建新线程
适用场景短期任务(避免频繁创建线程的开销)长期任务或需要独占线程的场景
性能风险若默认策略非异步,可能意外延迟执行频繁创建线程可能导致资源耗尽

2 详细对比分析

  1. 线程创建与执行
  • 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(); // 阻塞等待结果
      
  1. 返回值与结果获取
  • 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(); // 阻塞获取结果
      
  1. 异常处理
  • 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) { /* 处理异常 */ }
      
  1. 性能与资源消耗
  • 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::asyncstd::thread
结果获取自动通过 future需手动使用 promise 或共享变量
异常传播自动传递异常需手动捕获并处理
线程管理自动等待线程完成需显式调用 join()/detach()
灵活性适合简单任务适合需要精细控制的场景
性能优化可能复用线程(依赖实现)直接控制线程创建和销毁

关键原则:

  • 若需结果或异常安全,优先选择 std::async
  • 若需精细控制线程行为(如优先级、同步),使用 std::thread

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

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

相关文章

解决vscode在任务栏显示白色图标

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

架构思维:构建高并发扣减服务_分布式无主架构

文章目录 Pre无主架构的任务简单实现分布式无主架构 设计和实现扣减中的返还什么是扣减的返还返还实现原则原则一&#xff1a;扣减完成才能返还原则二&#xff1a;一次扣减可以多次返还原则三&#xff1a;返还的总数量要小于等于原始扣减的数量原则四&#xff1a;返还要保证幂等…

uni-app学习笔记九-vue3 v-for指令

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

MAC电脑中右键后复制和拷贝的区别

在Mac电脑中&#xff0c;右键菜单中的“复制”和“拷贝”操作在功能上有所不同&#xff1a; 复制 功能&#xff1a;在选定的位置创建一个与原始文件相同的副本。快捷键&#xff1a;CommandD用于在当前位置快速复制文件&#xff0c;CommandC用于将内容复制到剪贴板。效果&…

华为2025年校招笔试手撕真题教程(二)

一、题目 大湾区某城市地铁线路非常密集&#xff0c;乘客很难一眼看出选择哪条线路乘坐比较合适&#xff0c;为了解决这个问题&#xff0c;地铁公司希望你开发一个程序帮助乘客挑选合适的乘坐线路&#xff0c;使得乘坐时间最短&#xff0c;地铁公司可以提供的数据是各相邻站点…

征程 6 J6E/M linear 双int16量化支持替代方案

1.背景简介 当发现使用 plugin 精度 debug 工具定位到是某个 linear 敏感时&#xff0c;示例如下&#xff1a; op_name sensitive_type op_type L1 quant_dty…

深度学习模块缝合拼接方法套路+即插即用模块分享

前言 在深度学习中&#xff0c;模型的设计往往不是从头开始&#xff0c;而是通过组合不同的模块来构建。这种“模块缝合”技术&#xff0c;就像搭积木一样&#xff0c;把不同的功能模块拼在一起&#xff0c;形成一个强大的模型。今天&#xff0c;我们就来聊聊四种常见的模块缝…

改写视频生产流程!快手SketchVideo开源:通过线稿精准控制动态分镜的AI视频生成方案

Sketch Video 的核心特点 Sketch Video 通过手绘生成动画的形式&#xff0c;将复杂的信息以简洁、有趣的方式展现出来。其核心特点包括&#xff1a; 超强吸引力 Sketch Video 的手绘风格赋予了视频一种质朴而真实的质感&#xff0c;与常见的精致特效视频形成鲜明对比。这种独…

04-Web后端基础(基础知识)

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

Spring Cloud生态与技术选型指南:如何构建高可用的微服务系统?

引言&#xff1a;为什么选择Spring Cloud&#xff1f; 作为全球开发者首选的微服务框架&#xff0c;Spring Cloud凭借其开箱即用的组件、与Spring Boot的无缝集成&#xff0c;以及活跃的社区生态&#xff0c;成为企业级微服务架构的基石。但在实际项目中&#xff0c;如何从众多…

手写简单的tomcat

首先&#xff0c;Tomcat是一个软件&#xff0c;所有的项目都能在Tomcat上加载运行&#xff0c;Tomcat最核心的就是Servlet集合&#xff0c;本身就是HashMap。Tomcat需要支持Servlet&#xff0c;所以有servlet底层的资源&#xff1a;HttpServlet抽象类、HttpRequest和HttpRespon…

高等数学-积分

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

IOS平台Unity3D AOT全局模块结构分析

分析背景 由于IOS平台中不允许执行动态代码&#xff0c;Unity 4.6之前的版本在IOS平台中采用了AOT的处理方式&#xff0c;提前将C#代码静态编译为机器识别的二进制机器码。Unity引擎4.6之前的版本中IOS框架采用了Mono的AOT机制实现静态编译和处理&#xff0c;本文针对全局AOT模…

CyberSecAsia专访CertiK首席安全官:区块链行业亟需“安全优先”开发范式

近日&#xff0c;权威网络安全媒体CyberSecAsia发布了对CertiK首席安全官Wang Tielei博士的专访&#xff0c;双方围绕企业在进军区块链领域时所面临的关键安全风险与防御策略展开深入探讨。 Wang博士在采访中指出&#xff0c;跨链桥攻击、智能合约漏洞以及私钥管理不当&#x…

文件操作和IO-3 文件内容的读写

文件内容的读写——数据流 流是操作系统提供的概念&#xff0c;Java对操作系统的流进行了封装。 数据流就像水流&#xff0c;生生不息&#xff0c;绵延不断。 水流的特点&#xff1a;比如要100mL的水&#xff0c;可以一次接10mL&#xff0c;分10次接完&#xff0c;也可以一次接…

SpringAI 大模型应用开发篇-SpringAI 项目的新手入门知识

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 1.0 SpringAI 概述 目前大模型应用开发最常见的框架就是 LangChain&#xff0c;然而 LangChain 是基于 Python 语言&#xff0c;虽然有 LangChain4j&#xff0c;但是对于大量使…

编程速递-RAD Studio 12.3 Athens五月补丁:May Patch Available

编程速递-RAD Studio 12.3 Athens四月补丁&#xff1a;关注软件性能的开发者&#xff0c;安装此补丁十分必要 今天 &#xff08;2025 年 5 月 19 日&#xff09;Embarcadero 发布了 RAD Studio、Delphi 和 CBuilder 12.3 Athens&#xff08;雅典&#xff09;的第二个补丁。 RA…

Matlab学习合集

1.变量 2.常见的数学函数 3. 向量 向量的创建&#xff1a; 直接创建&#xff1a;针对于数量少的情况 冒号法 函数创建&#xff1a;

基于labview的声音采集与存储分析系统

基于LabVIEW的声音信号采集与存储分析系统开发实战&#xff1a;从原理到代码实现 &#xff08;内含源码&#xff09;基于labview的声音采集与处理系统 点击跳转工坊 点击跳转视频 引言 在音频技术与工业监测领域&#xff0c;声音信号的实时采集与分析是一项基础且关键的任务。…

【项目记录】部门增删改及日志技术

1 删除部门 1.1 需求 删除部门数据。在点击 "删除" 按钮&#xff0c;会根据ID删除部门数据。 了解了需求之后&#xff0c;我们再看看接口文档中&#xff0c;关于删除部门的接口的描述&#xff0c;然后根据接口文档进行服务端接口的开发。 1.2 接口描述 1.2.1 基…