如何在Boost.Asio C++20协程中实现自定义可等待(awaitable)对象
错误根因
你编写的是标准C++20原生可等待体,但boost::asio::awaitable协程框架的promise类型自定义了await_transform规则,仅支持符合Asio异步操作模型的等待对象,不支持直接co_await自定义原生可等待体,因此触发类型转换报错。
同时你直接通过std::coroutine_handle<>::resume()恢复协程的逻辑也存在线程安全风险:如果你的响应回调运行在非Asio io_context调度线程,会直接在回调线程执行协程后续逻辑,可能引发数据竞争。
可行解决方案
使用Boost.Asio官方提供的async_initiate工具,将回调式API包装为符合Asio规范的异步操作,可天然兼容awaitable协程,同时自动处理执行器上下文绑定、异常传递等逻辑。
代码修改步骤
- 重写
async_request包装逻辑:
#include <boost/asio/async_result.hpp> #include <boost/asio/use_awaitable.hpp> // 若编译器不支持C++20概念,可移除completion_token_for约束,直接写template <typename CompletionToken> template <boost::asio::completion_token_for<void(response)> CompletionToken = boost::asio::use_awaitable_t<>> auto async_request(client* cli, const request& r, CompletionToken&& token = {}) { return boost::asio::async_initiate<CompletionToken, void(response)>( [](auto completion_handler, client* cli, request req) { cli->send(req, [handler = std::move(completion_handler)](const response& resp) mutable { std::move(handler)(resp); }); }, token, cli, r ); }
这里为模板参数设置了use_awaitable作为默认 completion token,调用时不需要额外传参即可直接在awaitable协程中使用。
- 协程内调用逻辑:
boost::asio::awaitable<void> network::sts::client::connect() { //... // 直接co_await即可,不需要额外传参 auto res = co_await async_request(this, make_sts_connect()); //... }
额外注意事项
- 如果你的响应回调可能抛出异常,需要在回调内捕获异常,并通过
boost::asio::post将异常传递到协程执行上下文,避免直接触发std::terminate - 如果需要支持异步操作取消,可以在包装逻辑中集成Asio的取消槽机制,适配协程的自动取消逻辑
内容的提问来源于stack exchange,提问作者ACB
相关产品推荐
相关产品推荐

