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

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

文章目录

  • 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.xdnf.cn/news/534493.html

相关文章:

  • GPFS故障实际生产故障处理分析
  • 告诉我,pavucontrol的用法,我连接耳机的时候,输入设备应该使用什么呢?
  • AI神经网络降噪 vs 传统单/双麦克风降噪的核心优势对比
  • Ktransformers0.3框架的api访问接口程序
  • vue2.0 组件生命周期
  • LLaMA-Factory:了解webUI参数
  • Mysql 刷题 day06
  • Image and depth from a conventional camera with a coded aperture论文阅读
  • “保证医疗器械信息来源合法 真实、安全的保障措施、情况说明及相关证明”模板
  • 滑动窗口算法详解:从理论到实战(LeetCode 3 438)
  • 自动化测试的框架有哪些?原理是什么?
  • 深入掌握MyBatis:连接池、动态SQL、多表查询与缓存
  • springboot+mybatis或mybatisplus在进行%name%的前后模糊查询时如何放防止sql注入
  • 汇川MD810-20M4110GXXX变频器为什么要加GRJ9000S电源滤波器?
  • C# 深入理解类(属性)
  • python打卡day30
  • Navicat连接开启sm3认证的瀚高数据库
  • 网络请求和状态管理
  • SAP学习笔记 - 开发13 - CAP 之 添加数据库支持(Sqlite)
  • 《虚实共生:双向映射重塑具身智能决策逻辑》
  • 5.19 打卡
  • 存储系统02——Libevent事件循环
  • Interrupt 2025 大会回顾:关于LangChain 的 AI Agent会议内容总结
  • anythingLLM支持本地大模型嵌入知识库后进行api调用
  • Linux 系统异常触发后自动重启配置指南
  • 深入解析PyTorch中MultiheadAttention的参数key_padding_mask与attn_mask
  • 【AI时代】Java程序员大模型应用开发详细教程(上)
  • ALTER AGGREGATE使用场景
  • Pod 节点数量
  • 【Game】Powerful——Punch and Kick(12)