C++中std::async返回值怎么获取_C++异步编程future用法详解【指南】

8次阅读

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

C++中std::async返回值怎么获取_C++异步编程future用法详解【指南】

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_allc++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,调试难度高。

text=ZqhQzanResources