std::async不能直接串成任务链,因其返回的std::future不支持.then()等延续操作,且为单次消费、无调度器绑定,无法自然组合;需借助boost::asio或c++20协程等机制实现可组合异步链。

为什么 std::async 不能直接串成任务链
因为 std::async 返回的是 std::future,而标准库的 std::future 不支持 .then() 这类延续操作——调用 get() 会阻塞,手动轮询 wait_for() 又破坏异步流。你没法自然写出 f1.then([]{...}).then([]{...}) 这种表达。
常见错误是试图在主线程里连续调用 get():auto r1 = f1.get(); auto r2 = f2.get();,这本质是同步串行,和“异步链”完全背道而驰。
- 真正需要的是可组合(composable)的 future:能转发、映射、扁平化
-
std::future是“单次消费”且无调度器绑定,无法指定回调在哪执行(线程池?GUI 线程?) - C++20 的
std::jthread和std::stop_token解决的是取消,不是链式编排
用 boost::asio::post + 自定义 then 实现轻量链式调度
不引入 heavy 框架(如 folly 或 libunifex),用 boost::asio 的 io_context 和 post 就能搭出可靠的任务链底座。核心是把每个异步步骤包装为返回 boost::asio::awaitable<t></t> 或封装 std::shared_future<t></t> + 回调注册。
实操建议:
- 用
boost::asio::io_context统一管理线程池,避免裸std::thread泄漏 - 每个“链节”应接收前序结果,并返回新
std::shared_future或直接post到下一阶段 - 关键技巧:用
std::packaged_task包装 lambda,再用post投递,确保执行上下文可控
示例片段(非完整类,仅示意链节衔接):
auto step1 = std::make_shared<:promise>>();
auto fut1 = step1->get_future();
// 启动第一步(比如 IO)
boost::asio::post(io_ctx, [step1]{
step1->set_value(do_work_a());
});
// 第二步依赖第一步结果
auto step2 = std::make_shared<:promise>>();
auto fut2 = step2->get_future();
boost::asio::post(io_ctx, [fut1, step2]{
int res = fut1.get(); // 注意:此处 get() 安全,因已 post 到同 io_ctx,且 fut1 必已完成
step2->set_value(process(res));
});
</:promise></:promise>
避免 std::shared_future 生命周期陷阱
链式执行中最容易崩的是 future 对象提前析构,导致 get() 抛 std::future_error: broken promise。这不是并发问题,而是悬挂引用。
典型误写:
auto make_step() {
std::promise<int> p;
auto f = p.get_future();
boost::asio::post(io_ctx, [p=std::move(p)]{ p.set_value(42); });
return f; // ❌ p 已 move,f 成为 dangling future
}
</int>
正确做法:
- 永远用
std::shared_future代替std::future在链中传递 - 把
std::promise和其持有者(如链节点对象)生命周期绑定,例如用std::shared_ptr<node></node>管理整条链 - 若使用
boost::asio::awaitable,务必配合co_await,别混用get()—— 后者会挂起整个协程栈
何时该放弃手写链式模型,转用 libunifex 或 cppcoro
当你开始反复处理“异常传播”“取消信号透传”“多路合并(when_all)”或“超时包装(with_timeout)”,说明手写链已触达维护边界。这些不是逻辑问题,而是异步原语缺失。
现实判断点:
- 项目已用 C++20,且构建系统支持
libunifex(需 clang 15+ / MSVC 19.33+)→ 直接用unifex::then+unifex::schedule - 需要与 Windows I/O Completion Port 或 Linux io_uring 深度集成 →
cppcoro的win32_file或linux_eventfd更贴近底层 - 团队无协程调试经验,又必须支持旧编译器 → 宁可多写几个
post+shared_future节点,也别强行上co_await
手写链的价值在于理解调度本质;它的脆弱点也正在于此——少一个 shared_ptr 持有,整条链就静默失效。真正上线前,务必用 ASan + TSan 跑满所有链路分支。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











