std::future 没有 .then() 是标准库的明确取舍,仅负责单次结果传递;链式执行需手动管理 std::promise 生命周期、避免栈对象捕获、用 std::shared_ptr 保证存活,并通过 std::async 实现安全延续。

std::future 没有 .then(),不是设计缺陷,是标准库的明确取舍——它只负责“单次结果传递”,不承担“自动触发后续”的职责。想链式执行,必须自己补上调度逻辑,否则要么阻塞,要么崩溃。
为什么直接用 std::future + std::promise 写链会 crash
常见错误是把栈上 std::promise 捕获进异步 lambda,主线程函数返回后对象析构,子线程调用 set_value() 就触发 std::future_error(no_state)。还有人写 f1.get() 在构造 f2 时就执行,彻底变成同步串行。
-
std::promise必须存活到set_value()或set_exception()被调用完毕,推荐用std::shared_ptr<:promise>></:promise>管理 -
std::future只能调用一次get();第二次调用直接抛std::future_error(no_state) - 所有 lambda 捕获必须显式
[p = std::move(p), f = std::move(f)],std::future和std::promise都不可拷贝 - 别在主线程里等
f1.get()再启动f2,那不是异步,是假异步
最简可行的 then() 实现(C++11/14 兼容)
核心是用 std::async(std::launch::async, ...) 启动新任务,避免 std::thread::detach() 带来的生命周期失控风险。返回的 std::future 自动管理内部资源,比裸线程安全得多。
template<typename t typename f>
auto then(std::future<t> f, F&& func) -> std::future<decltype>()))> {
using R = decltype(func(std::declval<t>()));
auto p = std::make_shared<:promise>>();
auto ret = p->get_future();
std::async(std::launch::async, [f = std::move(f), func = std::forward<f>(func), p]( ) mutable {
try {
p->set_value(func(f.get()));
} catch (...) {
p->set_exception(std::current_exception());
}
});
return ret;
}</f></:promise></t></decltype></t></typename>
- 返回的
std::future<r></r>是 move-only,可继续传给下一个then() -
std::async启动的任务自带资源清理,不用detach(),也不用担心线程提前退出 - 注意:如果
func返回void,R就是void,std::promise<void></void>合法,但要调用p->set_value()(无参数) - 这个版本不支持重试、超时、取消——那是更高阶封装的事,别一开始就把
then()做成重型框架
std::shared_future 是唯一能“多消费者”的出路
如果你需要同一份结果被多个下游任务消费(比如日志记录 + 数据处理 + 缓存更新),std::future 会立刻失效;必须用 std::shared_future。
-
std::shared_future<t></t>可拷贝、可多次调用get(),底层共享同一状态机 - 从
std::future构造:std::shared_future<int> sf = f.share();</int>,之后f就不能再用 - 多个
then()并行消费同一个sf是安全的,但要注意:它们都依赖同一份输入,彼此之间没有执行顺序保证 - 别试图对
std::shared_future做链式then()的返回值——它本身不提供延续能力,只是解决“复用”问题
真正难的从来不是语法怎么写,而是谁来保证 std::promise 不提前死、异常不漏传、类型不擦除、线程不抢资源。每条链的起点和终点都得有人盯着生命周期,这是纯标准库方案绕不开的手工活。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











