std::async默认策略为std::launch::deferred,任务不真正异步执行,直到get()/wait()才同步运行;需显式指定std::launch::async强制并发,且异常仅通过get()捕获并重抛。

std::async 默认启动策略可能不异步
调用 std::async 后任务没真正并发执行?大概率是因为默认策略是 std::launch::deferred —— 任务压根不启动,直到你调用 get() 或 wait() 才在当前线程同步执行。这不是 bug,是标准允许的行为。
- 显式指定
std::launch::async强制异步:auto fut = std::async(std::launch::async, []{ /* work */ }); - 混合策略也可用:
std::launch::async | std::launch::deferred,让运行时决定(但实践中多数编译器仍倾向 deferred) - Clang 和早期 GCC 版本对
std::launch::async支持更严格;MSVC 通常按预期启动新线程
std::future.get() 会阻塞且只能调用一次
std::future 的 get() 是“取结果 + 消费 future”的原子操作:首次调用会阻塞直到就绪,之后再调用会抛出 std::future_error(错误码为 std::future_errc::no_state)。
- 别在循环里反复调
get()—— 第二次就崩溃 - 想轮询状态,用
wait_for(std::chrono::milliseconds(0))配合wait_status::ready判断 - 如果只关心完成不关心返回值,用
wait()更轻量;若需结果,get()是唯一入口
捕获异常必须通过 future.get()
异步任务里抛出的异常不会传播到主线程,而是被封装进 std::future。不调 get(),异常就一直“沉睡”,甚至程序退出都不会报告。
- 即使任务函数没 return,只要 throw,
get()就会 rethrow 原异常(类型不变) - 未 catch 的异常在
get()时直接终止当前栈帧,不是静默吞掉 - 常见误写:
std::async([]{ throw std::runtime_error("oops"); });—— 不接 future 或不调get(),等于白抛
lambda 捕获与生命周期必须手动管理
std::async 复制传入的可调用对象,所以 lambda 若按引用捕获局部变量([&x]),异步执行时 x 很可能已析构,导致未定义行为。
立即学习“C++免费学习笔记(深入)”;
- 优先值捕获:
[x] { use(x); },或用[x = std::move(x)]转移大对象 - 若真需引用外部资源,确保其生命周期覆盖整个异步执行期 —— 比如把对象存在
std::shared_ptr里,lambda 捕获该指针 - 注意:
std::async的参数也会被复制(非转发),传大对象前考虑std::ref或指针,但得自己担风险
最常被忽略的是 launch 策略和异常捕获时机——前者让代码看似异步实则串行,后者让崩溃悄无声息。这两点不确认清楚,std::async 很容易变成“假异步、真陷阱”。


















