std::execution Sender/Receiver 是比协程更底层的异步原语,不依赖栈切换与隐式挂起,提供编译期可组合、类型安全、无栈无分配、显式错误传递的标准化异步抽象,支持跨生态互操作。

std::execution Sender/Receiver 模型不是协程的替代品,而是更底层的异步原语
它不依赖栈切换、不隐式挂起、不引入运行时调度开销,本质是编译期可组合的回调抽象。协程(co_await)在 C++20 中是语法糖,其底层仍需依赖类似 Sender/Receiver 的机制来对接执行器(executor)和完成通知——C++26 的 std::execution 正是要标准化这个“底层”。你写 co_await,最终可能被编译成对 connect + start 的调用;而直接用 Sender/Receiver,则跳过协程帧分配和 ABI 约束,更适合零拷贝、硬实时或嵌入式场景。
组合性与类型安全远超协程链式 await
协程中连续 co_await 多个异步操作,实际形成的是线性控制流,难以表达并行、选择、重试等模式,且每个 awaitable 类型不统一,容易出现 no matching operator co_await 或模板推导失败。Sender/Receiver 则通过标准算法(如 then、let_value、on、transfer、when_all)提供可组合的、SFINAE 友好的管道式 DSL:
auto s = just(42)
| then([](int x) { return x * 2; })
| transfer(some_thread_pool)
| let_value([](int y) { return just(y + 1); });所有操作都在编译期检查签名兼容性(比如 then 的函数必须接收前一个 Sender 输出的值类型),错误信息指向具体连接点,而非模糊的 “awaitable concept not satisfied”。
无栈、无动态内存、可静态调度
默认情况下,Sender/Receiver 不强制分配协程帧,也不要求堆上分配状态对象。一个 just(3.14) Sender 可以是纯栈对象;connect 返回的 operation_state 也可 placement-new 在预分配缓冲区中。这使它能用于中断上下文、裸金属或内存受限环境。而协程默认生成带栈保存/恢复逻辑的帧,即使启用了 [[nodiscard]] 和 promise_type::get_return_object_on_allocation_failure,也无法完全规避动态分配风险。
立即学习“C++免费学习笔记(深入)”;
-
sender和receiver都是无状态或显式管理状态的类模板,不隐含生命周期绑定 - 调度策略(如
schedule_after、schedule_on)是独立的 Sender,可自由组合,不耦合到 awaitable 实现中 - 没有隐式异常传播路径:错误通过
set_error显式传递,避免协程中未捕获异常导致std::terminate
与现有生态的互操作不是靠适配,而是收敛为统一基元
协程要接入第三方异步库(如 libuv、Boost.ASIO、CUDA stream),得为每个库单独写 awaitable 包装器;而 Sender/Receiver 提供标准接口:connect、start、set_value、set_done。只要库实现这些定制点,就天然兼容 std::execution 算法——ASIO 已在 experimental 分支提供 as_sender,CUDA 也在推进 cuda::sender。协程做不到这点:你无法让 co_await cudaStreamSynchronize 直接参与 when_all,除非先把它封装成 Sender。
真正难的不是写一个 Sender,而是写出符合 sender concept 的、支持 transfer 跨执行域、支持 let_value 嵌套、且不泄漏资源的实现——概念约束严,但一旦满足,复用性和可推理性就远高于手写协程状态机。









