std::future只能移动不能拷贝,且get()/wait()后再次调用会抛std::future_error;链式调用须靠std::promise显式set_value/set_exception“推送”结果,而非反复取值。

std::promise 和 std::future 配合链式调用的核心约束
直接说结论:std::future 只能移动、不能拷贝,且一旦调用过 get() 或 wait(),后续再调用会抛 std::future_error(错误码为 std::future_errc::no_state)。这意味着“链式调用”不能靠反复取值实现,必须靠 std::promise 在前序任务完成时主动 set_value,把结果“推”给下一个 std::future。
常见错误现象:
- 对同一个
std::future多次调用get()→ 程序崩溃 - 在 lambda 中捕获
std::future并试图跨线程重复使用 → 未定义行为 - 忘记把
std::promise的get_future()提前传出去,导致下游阻塞等待空状态
如何用 std::promise 实现单层任务接力(非线程池版)
最简可行链:TaskA 完成后触发 TaskB,TaskB 依赖 TaskA 的返回值。关键不是“自动续传”,而是手动用 std::promise 桥接。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 每个任务对应一个独立的
std::promise<T>,其get_future()交给下游使用 - 前序任务执行完,必须显式调用
promise.set_value(result)(或set_exception),否则下游future.wait()永远不返回 - 避免在 lambda 中按值捕获
std::promise—— 它不可拷贝;应按引用或用std::shared_ptr<std::promise<T>>
示例(简化版):
std::promise<int> p1;
auto f1 = p1.get_future();
<p>std::thread t1([&p1] {
int res = heavy_computation();
p1.set_value(res); // 必须调用,否则链断裂
});</p><p>std::promise<std::string> p2;
auto f2 = p2.get_future();</p><p>// 下游等待 f1,并触发 f2 的 set_value
std::thread t2([&f1, &p2] {
try {
int x = f1.get(); // 此处阻塞直到 f1 就绪
p2.set_value(std::to_string(x * 2));
} catch (...) {
p2.set_exception(std::current_exception());
}
});</p>为什么 std::async 不适合直接构建任务链
std::async 返回的 std::future 是“一次性消费”的,且其生命周期与 launch policy 绑定:若用 std::launch::deferred,get() 才真正执行;若用 std::launch::async,线程已启动,无法中途注入回调。
典型陷阱:
- 写
auto f = std::async(...).then(...)→ 编译失败,std::future没有then成员函数(C++11/14/17 均不支持) - 试图用
std::async包裹前一个future.get()来“续跑” → 阻塞主线程,失去异步意义 - 多个
std::async间无显式依赖关系,调度顺序不确定,结果不可控
替代思路:用 std::packaged_task + std::thread 或队列管理执行时机,但需自行处理异常传递和状态同步。
实际工程中容易被忽略的异常传播细节
链式调用里,上游抛异常 ≠ 下游自动感知。如果前序任务没调用 promise.set_exception(),下游 future.get() 会等不到结果,而非抛出原始异常。
必须做的三件事:
- 所有任务逻辑包裹在
try/catch(...)中 - 在
catch块里统一调用promise.set_exception(std::current_exception()) - 下游
get()调用点也需try/catch—— 因为std::future::get()会 rethrow 上游设置的异常
漏掉任意一环,异常就静默吞掉或导致程序终止。
复杂点在于:不同任务可能在不同线程执行,std::current_exception() 是线程局部的,不能跨线程传递裸异常对象,必须靠 set_exception 封装。这点比看起来更易出错。


















