std::async默认策略不保证真异步,需显式指定std::launch::async;std::future::get()仅能调用一次且阻塞,未调用则析构时隐式wait;不支持多消费者,须用std::shared_future或std::move转移所有权;异常在get()时重抛。

std::async 启动异步任务时,launch策略决定是否真异步
默认情况下 std::async 不一定真正并发执行——它可能延迟到 get() 或 wait() 时才同步运行(std::launch::deferred 策略)。这常导致“以为异步,实则卡主线程”的问题。
- 显式指定
std::launch::async强制新开线程:auto fut = std::async(std::launch::async, []{ return 42; }); - 不指定策略时,编译器可自由选择;MSVC 默认倾向
deferred,GCC/Clang 多数情况走async,但不可依赖 - 混合使用两种策略时,注意资源竞争:多个
std::launch::async任务共享线程池(标准未规定池大小,实际由实现决定)
std::future::get() 只能调用一次,且会阻塞等待完成
get() 是获取结果的唯一合法方式,但它有强副作用:首次调用后,该 std::future 对象进入无效状态,再次调用会抛出 std::future_error(错误码为 std::future_errc::no_state)。
- 调用前可用
valid()检查是否还持有有效状态:if (fut.valid()) { auto res = fut.get(); } - 若只需等待完成、不关心返回值,用
wait()更安全 - 在析构前未调用
get()或wait(),会导致析构时隐式调用wait()—— 这可能让你在意外位置被阻塞
std::future 不支持多消费者,也不能跨线程转移所有权
std::future 是独占所有权类型,移动后原对象失效,且不能拷贝。这意味着它无法像 std::shared_future 那样被多个线程同时等待或取值。
- 需要共享结果时,改用
std::shared_future:auto fut = std::async([]{ return "done"; }); auto sf = fut.share(); - 想把 future 传给另一个线程处理?必须用
std::move:std::thread t([f = std::move(fut)]{ f.get(); }); - lambda 捕获 future 时也需显式移动:
[fut = std::move(fut)]() mutable { fut.get(); }
异常传播:异步函数抛异常,get() 会重新抛出
如果 async 中的函数抛出异常,std::future::get() 不会返回值,而是直接重抛该异常(类型不变)。这是 std::future 的核心设计之一,但容易忽略其传播时机。
立即学习“C++免费学习笔记(深入)”;
- 异常只在第一次
get()时抛出;后续调用仍报no_state - 若异步任务已抛异常但你只调用了
wait(),异常不会触发,直到你调用get() - 建议始终用 try/catch 包裹
get(),尤其在封装异步调用时:try { auto res = fut.get(); } catch (const std::exception& e) { /* 处理 */ }
真正麻烦的是:一旦 std::future 被 move 出作用域,又没在析构前 get() 或 wait(),程序就可能卡死在析构点——这个行为静默且难以调试。










