std::future 必须显式调用 get() 或 wait() 获取结果,否则析构时可能阻塞;get() 唯一取值且同步,调用后不可再调,异常会通过 get() 重抛,悬垂引用导致未定义行为。

std::async 返回的 std::future 必须显式获取结果,否则线程可能被阻塞在析构时(尤其用 std::launch::deferred 时);不调用 get() 或 wait() 就丢弃 future,是常见资源卡死源头。
std::future::get() 是唯一能取值且带同步语义的操作
调用 get() 会:① 阻塞直到异步任务完成;② 移动返回值(对右值引用类型);③ 释放内部共享状态。一旦调用过 get(),再次调用会抛出 std::future_error(错误码为 std::future_errc::no_state)。
常见误用:
- 重复调用
get()—— 程序崩溃 - 只调用
wait()不调用get()—— 值丢了,还可能泄漏线程资源 - 把 future 存在局部作用域却没取值 —— 析构时若任务未完成,
std::launch::async模式下会阻塞等待;std::launch::deferred下则直接在析构线程里同步执行(可能引发死锁)
std::async 启动策略影响 get() 行为和线程生命周期
std::async 默认使用 std::launch::async | std::launch::deferred 组合策略,但实际行为依赖实现。显式指定更可控:
立即学习“C++免费学习笔记(深入)”;
-
std::launch::async:一定新开线程,get()阻塞直到该线程完成 -
std::launch::deferred:不启动新线程,任务延迟到首次调用get()或wait()时,在当前线程同步执行
示例:
auto f1 = std::async(std::launch::deferred, []{ return 42; });
// 此时什么都没发生
f1.get(); // ← 这里才真正执行 lambda,并返回 42
注意:std::launch::deferred 下,wait() 不阻塞也不执行,只有 get() 或 wait_for()/wait_until() 的超时返回后紧接着调用 get() 才触发执行。
多个 future 怎么安全、高效地等结果?别裸写循环 wait
手动轮询 wait_for(std::chrono::milliseconds(1)) 效率低且易出错。优先用:
-
std::future_status status = fut.wait_for(timeout):非阻塞检查,返回ready/timeout/deferred -
std::when_all(C++20)或第三方库如folly::collectAll:批量等待多个 future - 对一组 future,用
std::vector<:future>>+ 范围 for +get(),但注意顺序阻塞 —— 第二个get()要等第一个完成后再发起等待(不是并发等)
如果真要并发等任意一个就绪,C++11/14 没原生支持,得靠条件变量 + 包装器模拟;C++20 起可用 std::when_any。
异常也会通过 future 传播,不 catch 就 terminate
异步任务中抛出的异常,不会导致程序崩溃,而是被捕获并存入 future 对象。但调用 get() 时会重新抛出:
auto f = std::async([]{ throw std::runtime_error("boom"); });
try {
f.get(); // ← 这里 rethrow
} catch (const std::exception& e) {
// 处理异常
}
关键点:
- 即使你忘了 try/catch,只要
get()被调用,异常就一定会抛出(不是静默吞掉) - 如果
get()永远不调,异常对象一直驻留,但不会影响其他线程 - 同一 future 只能
get()一次,异常也只 rethrow 一次
最常被忽略的是:lambda 捕获了局部对象的引用,异步执行时该对象已销毁 —— 这类悬垂引用问题不会在 get() 时报 future 相关错误,而是直接 undefined behavior,调试难度高。











