asio 1.22+ 启用协程需显式定义 asio_enable_concepts 和 asio_has_co_await,配合 -std=c++20 及 use_awaitable 令牌;async_read/write 需改用 co_await socket.async_xxx(..., use_awaitable) 并在 awaitable 函数中调用;线程跳变需 bind_executor 锁定执行器;错误处理推荐 redirect_error + deferred 拆包;对象生命周期必须覆盖协程全程。

asio 1.22+ 怎么启用 coroutine 支持
必须显式开启 ASIO_ENABLE_CONCEPTS 和 ASIO_HAS_CO_AWAIT,否则 co_await 会直接编译失败,报错类似 ‘await_transform’: is not a member of ‘asio::awaitable_executor’。C++20 协程不是开箱即用的“开关”,它依赖 asio 的底层适配器是否被激活。
实操建议:
- 在包含
<asio.hpp></asio.hpp>前定义:#define ASIO_ENABLE_CONCEPTS #define ASIO_HAS_CO_AWAIT
- 确保编译器支持 C++20(GCC 10+ / Clang 13+ / MSVC 19.30+),且开启
-std=c++20 - 链接时仍需
-lboost_system(除非用 asio 的 header-only 模式并禁用 OpenSSL)
如何把 async_read / async_write 改成 co_await
不能直接 co_await socket.async_read_some(...) —— asio 的传统异步函数返回 void,不满足可等待对象(awaitable)要求。必须用 asio::async_initiate 封装过的 awaitable 版本,或更简单:用 asio::use_awaitable 作为完成令牌(completion token)。
常见错误现象:写 co_await socket.async_read_some(buf, error); 报错 no operator "co_await" matches these operands。
实操建议:
- 把
asio::use_future或回调换成asio::use_awaitable:auto n = co_await socket.async_read_some( asio::buffer(data), asio::use_awaitable); -
use_awaitable要求函数调用上下文是asio::awaitable<t></t>,所以整个函数得声明为:asio::awaitable<void> handle_session(tcp::socket socket) { ... }</void> - 别漏掉
co_return;co_return;等价于return {};,但写成return;会编译失败
为什么 co_await 后线程可能跳变,怎么稳住执行上下文
asio 默认把协程恢复调度到任意可用的 io_context 线程上,这会导致变量捕获失效、TLS 错乱、或与外部同步逻辑冲突。这不是 bug,是 asio 的默认调度策略 —— 它只保证“在某个 io_context 关联的线程上恢复”,不保证“同一个线程”。
性能影响:跨线程恢复有轻微调度开销,但更重要的是语义风险 —— 比如你用 thread_local 缓存连接 ID,线程一换就丢了。
实操建议:
- 用
asio::redirect_error+asio::bind_executor锁定执行器:auto ex = co_await asio::this_coro::executor; co_await asio::bind_executor(ex, socket.async_read_some(buf, asio::use_awaitable));
- 更简洁的方式:在协程入口处保存 executor,并统一用
asio::bind_executor(ex, ...)包裹所有co_await调用 - 注意:
asio::this_coro::executor是 C++20 协程专用,不是传统get_executor()
error_code 怎么安全地从 co_await 中拿到
协程里不能像回调那样传入 error_code& 引用参数,也不能靠异常 —— asio 的 awaitable 默认把错误转为 system_error 抛出,但很多场景你只想检查、不希望崩溃(比如读到 EOF 是正常流程)。
使用场景:HTTP 请求中,async_read 返回 0 字节常表示对端关闭,这不是错误,但 co_await 默认抛异常会中断逻辑。
实操建议:
- 用
asio::deferred+asio::redirect_error拆包:auto [ec, n] = co_await ( asio::async_read(socket, buf, asio::redirect_error(asio::deferred, ec)) ); - 或者更推荐:用
asio::co_spawn启动协程时传入自定义错误处理器,避免层层try/catch - 别依赖
std::current_exception()捕获 —— asio 的 awaitable 错误路径不走标准异常传播链,容易漏判
协程和 asio 绑定最易被忽略的一点:它不自动管理内存生命周期。socket、buffer、甚至 promise 对象,如果在协程挂起期间被销毁,co_await 恢复时就是野指针。务必确保所有被协程捕获或引用的对象,生存期覆盖整个协程执行周期。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











