std::promise与std::future通过移动语义配对:用std::move传递promise给异步任务,future可拷贝但get()仅能调用一次;链式执行需手动用promise衔接,异常须显式set_exception()传播。

std::promise 和 std::future 怎么配对传递值?
不能靠拷贝,必须移动。promise 是一次性写入的,future 是一次性读取的,两者绑定后,promise 持有 shared state,future 只是它的观察者。常见错误是试图拷贝 std::promise 或在多线程中重复调用 set_value() —— 这会抛出 std::future_error(错误码为 std::future_errc::promise_already_satisfied)。
正确做法:
- 用
std::move()把 promise 传给异步任务(比如 lambda 或线程函数) - future 可以安全拷贝(内部是引用计数的 shared state),但只能调用一次
get() - 若需多次等待或广播结果,改用
std::shared_future
示例片段:
std::promise<int> p;
std::future<int> f = p.get_future();
std::thread([p = std::move(p)]() mutable {
std::this_thread::sleep_for(100ms);
p.set_value(42); // ✅ 只能调一次
}).detach();
// ... 后续可多次用 f.wait(),但 f.get() 只能调一次
</int></int>
怎么把多个 future 串成链式执行?
标准库没提供 .then(),得手动调度。核心思路是:前一个 future 完成后,用其结果构造下一个 promise,并触发新任务。关键不是“等 future”,而是“在 future ready 后立刻 dispatch 下一环”——否则容易阻塞线程或错过 ready 状态。
实操要点:
- 不要用
future.wait()主动轮询或阻塞;改用std::async或线程池 +future.wait_for(0s)检查状态 - 更可靠的方式是用条件变量 + 标志位模拟回调,或借助第三方(如 libunifex);但纯标准库下,推荐用
std::thread+std::future::wait()+ detach 组合 - 链中每个环节都应持有自己的
std::promise,并把上一环的future.get()结果传给下一环的 callable
简化链式结构示意:
auto chain = [](auto&& f1, auto&& f2) {
return [f1 = std::move(f1), f2 = std::move(f2)](auto&&... args) {
auto p = std::make_shared<:promise>()))>>();
auto fut = p->get_future();
std::thread([p, f1, f2, ...args = std::move(args)]() mutable {
try {
auto res1 = f1(std::move(args)...);
auto res2 = f2(std::move(res1));
p->set_value(std::move(res2));
} catch(...) { p->set_exception(std::current_exception()); }
}).detach();
return fut;
};
};
</:promise>
为什么链式执行中异常传播经常失败?
std::promise::set_exception() 是唯一能跨线程传递异常的方式,但很多人只记得 set_value(),漏掉异常分支。一旦异步任务抛异常且未被捕获,线程终止,promise 不会被设置,下游 future.get() 就永远卡住(除非设了超时)。
必须显式处理:
- 所有异步 callable 内部用
try/catch包裹业务逻辑 - 在
catch块里调用p.set_exception(std::current_exception()) - 下游调用
future.get()时,异常会原样 rethrow,注意栈回溯丢失问题(C++17 起部分实现保留) - 别依赖析构 —— promise 析构时不自动 set_exception,也不会通知 waiting future
std::future 链式执行的性能和生命周期陷阱
每个 std::future 背后是共享状态(shared state),频繁创建/销毁 promise-future 对会带来堆分配开销;更严重的是,future 的 get() 是阻塞点,如果链中某环忘记 move 或意外拷贝,可能引发悬挂引用或 double-get。
容易被忽略的细节:
-
std::future移动后变为 invalid 状态,再次调用get()会抛std::future_error(no_state) - 使用
std::async默认是std::launch::async | std::launch::deferred,后者可能导致get()同步执行,破坏异步语义 - 没有显式
detach()或join()的线程,其std::thread对象析构会 terminate —— 链式任务里常因 scope 提前结束而踩坑
真实项目中,建议封装一层 Task 类,统一管理 promise 生命周期、异常路由和线程归属,避免裸用 std::promise/std::future。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











