C++中的期值(std::future/promise)是什么?(如何获取异步结果)

1次阅读

std::future 通过 get() 阻塞获取一次性结果,仅能调用一次;需确保 promise 正确设置值且生命周期覆盖 future 使用期,配合 wait_for 可避免无限等待。

C++中的期值(std::future/promise)是什么?(如何获取异步结果)

std::future 怎么拿到异步计算的结果

它不是“自动触发”的返回值,而是一个**一次性取用的同步门把手**。你调用 get() 才真正阻塞等待、获取结果(或异常),且只能调一次——第二次调会抛 std::future_error(错误码为 std::future_errc::no_state)。

常见错误现象:反复调 get()、在没确认 future 有值时就调用、把 future 当普通变量传值后又想在原处取值(移动后原对象已无效)。

  • get() 是阻塞的,线程会挂起直到结果就绪;若 promise 永远不 set_value/set_exception,就永远卡住
  • move 语义很关键:把 future 传给另一个函数后,原变量变为空状态,再调 get() 就崩溃
  • 支持 wait_for()wait_until() 做带超时的等待,避免无限阻塞

std::promise 怎么和 std::future 配合用

promise 是“写入端”,future 是“读取端”,二者通过共享状态(shared state)绑定。一个 promise 只能调一次 set_value()set_exception(),之后该 shared state 就算完成。

使用场景:需要手动控制异步任务完成时机,比如封装回调式 C API、实现超时重试逻辑、或跨线程传递错误。

立即学习C++免费学习笔记(深入)”;

  • 不能用默认构造的 std::promise<int></int> 直接 get_future() —— 必须先声明 promise,再调 get_future() 拿 future
  • promise 对象本身可拷贝(但通常 move),但它的 shared state 不可重复绑定;同一个 promise 多次 set_value() 会抛异常
  • 如果 promise 析构前没调 set_value()set_exception(),其 shared state 会以异常方式结束(std::future_error with broken_promise

async()、packaged_task、promise 三种获取 future 的方式怎么选

它们都产出 std::future,但控制粒度和适用场景完全不同。

  • std::async() 最省事:自动创建线程、执行函数、返回 future;但无法控制线程生命周期,且默认策略(std::launch::async | std::launch::deferred)可能延迟执行,调试时容易误判“没跑”
  • std::packaged_task 适合需要多次复用执行逻辑的场景,比如放进队列、绑定到事件循环;它把 callable 包装成可调用对象,调用它即触发 set_value
  • std::promise 最底层、最灵活:你能完全掌控何时、在哪、用什么值/异常去完成 future;适合封装非标准异步模式(如信号、I/O 完成端口、第三方回调)

future.get() 卡死?怎么排查是不是 promise 没被触发

最常见原因是 promise 对象提前析构,或根本没被任何线程调用 set_value() —— 此时 future 永远等不到信号。

性能影响不大,但逻辑错误极难定位:看起来像死锁,其实是“没人敲门”。c++ 标准不提供检查 future 是否 ready 的廉价方法(wait_for(0s) 是唯一办法)。

  • future.wait_for(std::chrono::seconds(0)) == std::future_status::ready 判断是否就绪,避免盲等
  • 确保 promise 生命周期 ≥ future 使用周期;常见坑是局部 promise 在 async Lambda 里声明,lambda 捕获的是值,promise 随 lambda 返回而销毁
  • 别依赖 future 的析构来释放资源——shared state 的清理是自动的,但业务资源(如 socket、buffer)得你自己管

shared state 的生命周期由最后一个 future 或 promise 控制,这点容易被忽略:哪怕你只留了一个 future,它不 get、不 wait,state 就一直占着内存,还可能拖住线程。

text=ZqhQzanResources