std::async默认采用std::launch::deferred策略,调用get()才同步执行,并非真正异步;需显式指定std::launch::async才能启动新线程立即执行,但要注意开销与异常处理。

std::async 默认不是非阻塞的,必须显式指定 launch policy
很多人以为调用 std::async 就自动“后台跑”,结果主线程一碰 get() 就卡死——根本原因是默认行为是 std::launch::deferred(延迟执行),也就是“你调 get() 时才真正运行”,完全不是并行。
要真正启动新线程、立刻异步执行,必须显式传 std::launch::async:
auto fut = std::async(std::launch::async, []{ return heavy_work(); });
- 不传 policy 或传
std::launch::deferred:函数不执行,get()触发同步调用 - 只传
std::launch::async:强制新开线程,立即执行(但可能因系统资源失败) - 传
std::launch::async | std::launch::deferred:让标准库自己选,但get()仍可能同步——不可靠,别依赖
任务拆分后怎么安全取结果?别直接 get() 等着
想“非阻塞”就意味不能在主线程里堵住等结果。常见错误是循环调 fut.get(),或者没检查状态就强取。
正确做法是用 wait_for() 或 wait_until() 主动轮询,配合 ready() 判断:
立即学习“C++免费学习笔记(深入)”;
auto fut = std::async(std::launch::async, []{ return 42; });
// 非阻塞检查
if (fut.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) {
int res = fut.get(); // 此时才安全取值
}
-
get()是一次性消费操作,调完 future 就失效,重复调会抛std::future_error -
wait_for(0ms)是唯一轻量级“试探是否就绪”的方式;用wait()又变阻塞了 - 多个 future 时,别手写轮询逻辑——考虑
std::shared_future或改用std::packaged_task+ 手动线程池更可控
捕获异常必须在 get() 里处理,否则直接 terminate
async 启动的任务如果抛异常,不会冒泡到主线程,而是被存进 future 对象里——但如果你忘了调 get(),程序在 future 析构时会直接调 std::terminate(),静默崩溃。
- 哪怕你只关心结果是否完成,也得至少调一次
get()或wait()来“收尸”异常 - 推荐统一用 try/catch 包住
get():
try {
auto res = fut.get();
} catch (const std::exception& e) {
// 异常来自 async 里的 lambda
}
- lambda 内未捕获的异常、
std::bad_alloc、甚至断言失败,都会被封装为std::exception_ptr存进去 - 不处理=进程退出,没有警告,调试时极难定位
std::async 不适合高频短任务,线程创建开销比工作本身还大
每次 std::async 都可能新建线程(取决于 policy 和实现),而线程创建/销毁成本在毫秒级。如果你拆的是几十微秒的计算,比如向量加法、简单字符串处理,用 async 反而更慢。
- 典型误用场景:在一个循环里对每个数组元素调
std::async - 替代方案:用
std::thread手写固定线程池,或用 OpenMP(#pragma omp parallel for) - 即使只跑几个长任务,也要注意:Windows 下 MSVC 的
std::async实现曾有调度延迟问题,Linux+libstdc++ 相对稳定
async 的价值在于“逻辑上解耦 + 一次性的中长耗时任务”,不是通用并行加速器。拆任务前先看单次耗时是否 >1ms,否则大概率白忙。










