结论:C++11/14 的 std::future 不支持链式调用,因 get()/wait() 后失效且无 .then(),易致 No state 错误或悬挂 promise;需用 std::shared_ptr 管理 std::promise 生命周期并手动编排任务。

直接说结论:用 std::promise 和 std::future 拼链式异步任务,不是不行,但必须手动管理生命周期和线程安全;稍不注意就会出现 std::future_error: No state 或悬挂 promise。
为什么不能直接 chain std::future?
C++11/14 的 std::future 不支持 .then() 这类延续操作,调用 get() 或 wait() 后它就“消耗”掉了;多次调用会抛 std::future_error: No state。标准库没提供 move-only 的可延续 future 类型(C++20 有 std::shared_future 缓解部分问题,但仍不解决链式触发)。
常见错误现象:
- 把同一个
std::future传给两个 lambda,第二个调用get()直接崩溃 - 在 lambda 中捕获局部
std::promise对象,异步执行时对象已析构 → 未定义行为 - 用
std::async启动任务但没保存返回的std::future,导致任务被阻塞等待(std::async默认是 lazy 吗?不,是 deferred —— 实际取决于 launch policy)
手动实现单级 continuation 的最小可行模式
核心思路:前一个任务完成时,用 std::promise 设置下一个任务的输入,再用 std::async 或线程启动它。关键是要让 promise 的生命周期撑到 set_value 被调用。
实操建议:
- 永远用
std::shared_ptr<:promise>>代替栈上std::promise,避免异步 lambda 捕获悬空引用 - 前序任务里不要直接调用
promise->set_value(...),而是包装进std::async(std::launch::async, ...)或投递到线程池,否则可能阻塞当前执行流 - 如果前序任务返回 void,后续 promise 类型应为
std::promise;类型不匹配会导致编译失败(比如试图set_value(42)到promise<:string>)
简短示例(两段式异步链):
auto p1 = std::make_shared<:promise>>();
auto f1 = p1->get_future();
auto p2 = std::make_shared<:promise>>();
auto f2 = p2->get_future();
// 第一段:计算 int
std::async(std::launch::async, [p1]{
std::this_thread::sleep_for(100ms);
p1->set_value(123);
});
// 第二段:接收 int,转 string
std::async(std::launch::async, [p1, p2]{
auto val = p1->get_future().get(); // 注意:这里会阻塞直到 p1 完成
p2->set_value("got: " + std::to_string(val));
});
</:promise></:promise>
如何避免 future/promise 生命周期陷阱?
最常被忽略的一点:std::promise 对象本身不线程安全——set_value、set_exception 等只能调用一次,且不能在已调用后再次调用;而 std::future::get() 是线程安全的,但仅限读取。
使用场景与参数差异:
- 若任务可能失败,务必用
promise->set_exception(std::current_exception()),而不是忽略异常,否则下游get()会直接 terminate - 不要用
std::async(std::launch::deferred, ...)做链式触发起点——它不会立刻执行,get()才触发,完全失去异步意义 - 跨线程传递
std::future必须 move(std::move(f)),因为std::future是 move-only 类型;拷贝编译不过 - 调试时可加断言:
assert(!f1.valid());在get()后检查是否已失效,快速定位重复 get
真正实用的链式替代方案(不造轮子)
如果你需要生产级链式异步(比如 HTTP 请求 → 解析 → 存 DB),别硬刚原生 promise/future。现实中的选择更可能是:
- 用
boost::asio::awaitable+co_await(C++20 协程支持完善后) - 接入
libunifex或cppcoro,它们提供了then、let_value等组合子 - 轻量级封装:写一个
Task类,内部持有一个std::shared_ptr<:promise>>和执行函数,重载operator|实现类似t1 | process | save的写法(但要注意 operator| 返回新对象,别改原 task)
复杂点在于:链式执行不是单纯串行,还要处理并发分支、取消传播、超时控制——这些原生 promise/future 都不提供,得自己补全。而补全的过程,往往比换一个成熟异步框架更耗时。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











