asio::co_spawn 的完成处理器会在哪个 executor 上执行?
结论
按示例中的普通回调用法,协程完成后,co_spawn 的完成回调会在传给 co_spawn 的 ex 上执行。因此,回调中的 std::move(h)({}, std::move(value)); 也会沿这条执行路径调用,通常无需再用 asio::post(ex, ...)。
这里的 h 是 async_initiate 根据 completion token 转换得到的完成处理器。co_spawn(ex, ...) 使用 ex 调度协程,而示例中的包装 lambda 会在 co_spawn 完成时直接调用 h。Boost.Asio 文档将 ex 描述为用于调度新协程执行线程的 executor。co_spawn 文档
什么时候需要显式投递
如果要明确增加一次异步排队,可以把调用放进 post:
asio::post(ex,
[h = std::move(h), value = std::move(value)]() mutable {
std::move(h)({}, std::move(value));
});
这样会多一次排队,也会改变回调的执行时机。通常无需仅为让回调在 ex 上运行而这样做。post 不保证回调总由某个固定线程执行:如果多个线程同时调用同一个 io_context::run(),它们都可能执行该 executor 上的任务。
关联 executor 的注意事项
如果调用者传入了带自定义关联 executor 的处理器,例如通过 bind_executor 将处理器绑定到另一个 executor,当前包装 lambda 并没有显式转发 h 的关联属性。因此,包装回调在 ex 上执行,并不意味着通用异步操作已经正确保留了调用者处理器的关联 executor。Asio 异步操作规范要求完成处理器按其关联 executor 投递;关联 executor 的默认值取决于操作的 executor 和处理器本身。异步操作要求
若此封装需要支持自定义关联 executor,应在设计中保留并遵循处理器的关联属性。具体实现可根据要支持的 completion token 和 Asio 版本,选择合适的关联器转发方式。如果接口约定无论调用者如何绑定,都强制在 ex 上完成,应明确说明这一点。
额外检查
示例中的 context 没有在片段里定义。如果它不是外围作用域中的对象,捕获 [&context] 就无法编译。另外,错误分支使用 T{},这要求 T 可默认构造。这两点不影响 executor 调度结论,但会影响这段代码能否用于任意 T。
备注:内容仅供参考。
