不能。std::future不可拷贝、只读且仅能get()一次,不支持then链式调用;需用std::async+mutable lambda捕获并move前序future实现流水线,三阶段以上应避免嵌套而采用链式命名与显式move。

std::future 能不能直接串成流水线
不能。std::future 本身是只读、单次获取结果的句柄,不支持 then 链式调用,也不能被多次 get() 或重复等待。想“把 A 的输出自动喂给 B”,得自己桥接 —— 最常用的方式是用 std::thread 或 std::async 启动后续任务,并在其中显式调用前序 get()。
最简可行流水线:两阶段 + std::async + lambda 捕获
核心思路:用 lambda 封装前一个 std::future 的消费逻辑,并把它交给新异步任务执行。注意必须用 std::move 转移前序 future,否则编译失败(std::future 不可拷贝)。
auto stage1 = std::async(std::launch::async, []{ return 42; });
auto stage2 = std::async(std::launch::async, [fut = std::move(stage1)]() mutable {
int x = fut.get(); // 必须 mutable,因为 get() 是非 const 成员函数
return x * 2;
});
int result = stage2.get(); // → 84
- lambda 参数捕获
fut = std::move(stage1)是关键,避免悬空引用 - 必须加
mutable,否则fut.get()编译不过 - 两个
std::async默认都用std::launch::async,确保真正并发(默认策略可能延迟执行)
三阶段及以上怎么避免嵌套地狱
嵌套 lambda 很快变得难读。推荐提取为独立函数或使用局部变量链式赋值,保持每阶段 future 名字清晰:
auto a = std::async([]{ return 10; });
auto b = std::async([f=std::move(a)]() mutable { return f.get() + 5; });
auto c = std::async([f=std::move(b)]() mutable { return f.get() * 3; });
- 每个
std::async都显式 move 前一个 future,不能省略 - 不要写成
auto c = std::async([a,b](){...})—— 这样 a 和 b 未被 move,第二次 get 会抛std::future_error(错误码std::future_errc::no_state) - 如果某阶段需要多个输入(如 merge 两个流),就得手动 wait_all 或用
std::shared_future多次 get,但那就不是标准 future 流水线了
为什么不用 std::promise 手动控制更灵活
因为 std::promise + std::future 配对能解耦“谁设置结果”和“谁消费结果”,更适合复杂依赖。比如 stage2 想等 stage1 和 stage1_alt 都完成再启动:
立即学习“C++免费学习笔记(深入)”;
std::promise<int> p2; auto f2 = p2.get_future(); // … 启动 stage1 和 stage1_alt // 在它们都完成后的回调里:p2.set_value(val1 + val2);
-
std::promise允许你在任意线程、任意时机调用set_value,比纯std::async链更可控 - 但代价是手动管理生命周期:promise 对象必须在线程安全范围内存活到 set_value 被调用
- 一旦忘记 set_value 或重复 set_value,future 会永远阻塞或抛异常 —— 这是最容易漏掉的崩溃点
实际写多阶段流水线时,move 语义、mutable lambda、future 状态销毁顺序这三点出错频率最高,建议每新增一阶都加个 assert(fut.valid())。


















