【Effective Modern C++】第七章 并发API:35. 优先考虑基于任务的编程而非基于线程的编程

两种异步执行方式的直观对比

基于线程(thread-based):直接创建 std::thread
c++ 复制代码
int doAsyncWork();
std::thread t(doAsyncWork); // 直接创建线程执行函数
  • 无直接获取返回值的方式;
  • 若函数抛出异常,程序会直接调用std::terminate终止。
基于任务(task-based):使用 std::async
c++ 复制代码
auto fut = std::async(doAsyncWork); // 提交任务,返回future对象
  • 代码更简洁;
  • future的get()函数可获取返回值,还能捕获函数抛出的异常(避免程序终止);
  • 线程管理的责任交给标准库,而非开发者。

理解线程的三层含义

线程类型 定义
硬件线程 CPU 核心提供的真实执行单元,是计算的物理载体。
软件线程(OS 线程) 操作系统管理的线程,运行在硬件线程上,数量可多于硬件线程(阻塞时 OS 调度其他线程)。
std::thread C++ 中 "软件线程" 的句柄,可能为空(默认构造、移动、join、detach 后无对应软件线程)。

基于线程编程的痛点

直接使用std::thread的核心问题在于需要开发者手动处理线程管理的复杂问题:

  • 线程资源有限 :系统支持的软件线程数量有上限,创建超出限额的std::thread会抛出std::system_error异常;即使函数本身noexcept,也可能触发此异常,且异常处理逻辑复杂(比如回退到当前线程执行会导致负载不均、GUI 线程响应慢)。

  • 资源超额(oversubscription) :可运行的软件线程数 > 硬件线程数时,OS 会对线程做时间切片,引发上下文切换:

    • 上下文切换本身增加系统开销;
    • 若线程切换到不同硬件核心,会导致 CPU 缓存失效(无可用数据 / 指令),还会 "污染" 原核心的缓存,进一步降低性能。
  • 优化难度极高:软件线程与硬件线程的最佳比例是动态变化的(比如程序从 IO 密集型变为计算密集型),且依赖硬件特性(缓存大小、核心数),跨平台适配几乎不可能。

基于任务编程的优势

std::async的价值是将线程管理的责任交给标准库,规避上述痛点:

  • 灵活的调度策略 :默认启动策略下,std::async不保证创建新线程;当系统线程耗尽 / 资源超额时,会将任务调度到 "等待结果的线程"(调用fut.get()/fut.wait()的线程)上执行,避免线程创建失败或资源超额。
  • 更智能的运行时调度 :标准库调度器(部分实现采用线程池 + 工作窃取算法)比开发者更了解系统全局状态(所有执行过程),能更好地实现负载均衡,优化上下文切换和缓存利用。
  • 天然支持结果 / 异常处理 :future的get()函数可获取异步执行的返回值,或捕获函数抛出的异常,避免程序终止。

仍需使用 std::thread 的特殊场景

  • 需要访问底层线程 API:std::thread提供native_handle()成员函数,可操作线程优先级、亲和性等(std::future无此能力);
  • 可精准优化线程使用:比如部署在固定硬件上、执行概况明确的服务器(作为唯一关键进程);
  • 实现标准库不支持的线程技术:比如特定平台未被 C++ 标准库实现的线程池。

总结

  1. 基于线程的编程(std::thread)需要手动处理线程耗尽、资源超额、负载均衡等复杂问题,且无法直接获取异步执行结果,函数异常会导致程序终止;
  2. 基于任务的编程(std::async)将线程管理交给标准库,默认启动策略可规避大部分线程管理痛点,还能通过 future 获取结果 / 捕获异常;
  3. 仅在需要访问底层线程 API、精准优化线程使用或实现非标线程技术时,才考虑直接使用 std::thread。

原著在线阅读地址

相关推荐
汉克老师5 小时前
《从C++小白到算法高手:GESP 1—8级成长地图》第一讲:认识GESP——从编程新手到算法探索者
c++·gesp·信息学竞赛·小学生学c++
晴天的雨.9926 小时前
【C++】继承篇
java·开发语言·c++
青少儿编程课堂7 小时前
蓝桥杯青少组图形化 + 全国青少年信息素养大赛 + 电子学会图形化考级:校园大富翁经营闯关解析
c++·python·算法·bfs·信息学竞赛
another heaven7 小时前
【C/C++ 第一次跨越(上):用 C++11/14 停止制造新的旧代码】
c语言·c++·制造
qq_406981037 小时前
c++ lamda(三)Lambda表达式
java·前端·c++
I_belong_to_jesus17 小时前
C++关于分支预测的优化1
开发语言·c++·算法
Rabitebla19 小时前
【Linux】进程控制(十二) 自定义 Shell(二):内建命令与进程工作目录管理
linux·c++·笔记·学习·bash
码客-ygluu1 天前
一个通用游戏数据服务器
c++·架构
another heaven1 天前
【C/C++ Ranges 是零开销抽象吗?拆开管道、惰性求值和编译期膨胀】
c语言·开发语言·c++
YLCHUP1 天前
【思维&图论建模】题解:债权(debt)_图论_建模_思路转化_思维题目_C++算法竞赛
c++·笔记·算法·数学建模·贪心算法·深度优先·图论