C++20前std::future无then,且当前编译器未实现shared_future::then;需手动用std::promise+回调实现链式异步,注意线程安全、生命周期和异常传播。

std::future 没有 then,别硬写封装接口
标准 C++20 之前,std::future 确实不支持 then;C++20 引入了 std::future::then,但仅限于 std::shared_future,且要求执行器(executor)支持——而目前主流编译器(GCC 13/Clang 17)的 libstdc++ 和 libc++ **尚未实现该接口**。你看到的所谓“std::future::then”调用,99% 是编译失败或链接时报 undefined reference to 'std::future<...>::then(...)' </...>。
所以别在生产代码里写 my_future.then(...) 并指望它能过编译。真要链式异步,得自己搭骨架,或换库(如 boost::fibers、libunifex),或等 C++23 后更成熟的 executor 框架落地。
手动实现任务链:核心是 std::promise + 回调转发
最轻量、不依赖第三方的方案,是用 std::promise 手动桥接前序 std::future 的完成与后续任务的触发。关键不是“包装 then”,而是“谁来 resolve 下一个 promise”。
- 前一个
std::future完成后,必须在某个线程(可能是新线程、线程池、或原线程)中调用下一个std::promise::set_value()或set_exception() - 不能直接在
future.wait()后同步调用——这会阻塞,破坏异步性;得用std::thread、std::async或已有线程池触发后续逻辑 - 注意
std::promise生命周期:必须确保 promise 对象在 set_xxx 被调用前未析构(常见坑:promise 是栈变量,而回调在线程中异步执行)
示例片段(无异常处理简化版):
立即学习“C++免费学习笔记(深入)”;
template <typename T, typename F>
auto then(std::future<T>&& f, F&& func) -> std::future<std::invoke_result_t<F, T>> {
std::promise<std::invoke_result_t<F, T>> p;
auto ret = p.get_future();
// 启动新线程执行链式逻辑(实际应交由线程池)
std::thread([f = std::move(f), func = std::forward<F>(func), p = std::move(p)]() mutable {
try {
auto val = f.get(); // 阻塞在此,但在线程内
p.set_value(func(std::move(val)));
} catch (...) {
p.set_exception(std::current_exception());
}
}).detach(); // 注意:detach 带风险,应管理线程生命周期
return ret;
}用 std::async 实现无锁链式调用(适合简单场景)
如果你的任务链较短、无高频调度需求,std::async 是更安全的选择:它自动管理 std::promise 生命周期,且默认使用系统线程(或延迟执行策略),避免手动 detach 导致的悬空。
-
std::async返回的std::future保证其内部std::promise在 future 析构前有效 - 不要用
std::launch::deferred——它会把执行推迟到.get(),破坏链式时序 - 嵌套
std::async会产生额外线程开销,5 层以上链建议改用单线程事件循环或协程
链式调用示意:
auto f1 = std::async([]{ return 42; });
auto f2 = std::async(std::launch::async, [f1 = std::move(f1)]() mutable {
return f1.get() * 2;
});
auto f3 = std::async(std::launch::async, [f2 = std::move(f2)]() mutable {
return std::to_string(f2.get());
});注意:这里每个 std::async 都是独立 future,需显式 move 捕获前序 future,否则编译失败(f1 不可拷贝)。
真正易被忽略的点:异常传播与取消传递
手写链式调用时,std::future::get() 抛出的异常若没被捕获并转为 std::promise::set_exception(),会导致程序 terminate——因为未处理的异常从线程出口逃逸。
更隐蔽的问题是取消(cancellation):标准 std::future **不支持取消通知**。前序任务中途被“取消”,后续 promise 不会自动失效;你得自己设计 cancel token(如 std::atomic_bool*)并让每个 lambda 主动轮询。
所以:链越长,越要警惕两点——
- 每个
try/catch必须覆盖f.get()和func(...)两处可能抛异常的位置 - 若需取消语义,别依赖 future 本身,改用
std::stop_token(C++20)配合std::jthread,或引入folly::SemiFuture这类带取消能力的类型
没有运行时取消机制的任务链,本质上只是“串行异步”,不是真正的可中断异步流。


















