co_await不能直接等待裸文件描述符或socket,必须作用于实现awaitable协议的对象;标准库未提供i/o原生支持,需用boost.asio(启用boost_asio_has_co_await)或自定义封装,其核心是正确实现await_ready/await_suspend/await_resume三函数。

co_await 本身不支持直接等待文件描述符或 socket
很多人一上来就写 co_await fd 或 co_await socket,结果编译失败——co_await 不是万能胶水,它只对实现了 awaitable 协议的对象生效。裸的 int 文件描述符、SOCKET 句柄、甚至 std::future(C++20 中默认不可 await)都不满足要求。
真正能 co_await 的,必须提供 await_ready()、await_suspend() 和 await_resume() 三个成员函数。标准库没为 I/O 提供现成实现,得自己搭或用第三方库。
- Linux 下常用
io_uring+ 自定义awaitable封装(需内核 5.1+) - Windows 下可用
IOCP+winrt::Windows::System::Threading::ThreadPool或自定义 suspend/resume - 跨平台方案:Boost.Asio 的
awaitable(需启用BOOST_ASIO_HAS_CO_AWAIT)
Boost.Asio 是目前最稳妥的 C++20 co_await I/O 路径
Boost 1.76+ 完整支持 C++20 协程,且封装了底层异步机制(epoll/kqueue/IOCP),不用手撸 await_suspend 里的线程调度逻辑。
关键前提是链接时开启协程支持:
#define BOOST_ASIO_HAS_CO_AWAIT #include <boost> #include <boost> #include <boost></boost></boost></boost>
常见错误:忘记定义宏,或头文件顺序不对(BOOST_ASIO_HAS_CO_AWAIT 必须在 #include <boost></boost> 前)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
boost::asio::ip::tcp::socket和boost::asio::posix::stream_descriptor都已重载operator co_await - 读操作示例:
auto n = co_await sock.async_read_some(buf, boost::asio::use_awaitable) - 写操作同理,但注意
async_write是复合操作,需用boost::asio::async_write+use_awaitable,不能直接co_await sock.async_write(...)
手动实现 awaitable 时,await_suspend 返回值决定执行流去向
这是最容易出错的一环:返回 std::coroutine_handle 表示挂起并移交控制权;返回 false 表示不挂起(立即 resume);返回 true 是未定义行为(标准禁止)。
实际 I/O 等待中,你通常要注册回调(如 epoll_ctl 或 WSARecv),并在回调触发后调用 handle.resume()。漏掉这一步,协程永远卡住。
- Linux
io_uring场景下,await_suspend应提交 SQE 并返回false(因内核完成时自动唤醒) - 传统 epoll 场景下,
await_suspend必须把coroutine_handle存入事件循环的 pending 队列,并确保可被epoll_wait后回调唤醒 - 别在
await_suspend里做阻塞操作(如read()),否则整个协程调度器线程被拖垮
协程栈和异常传播在 I/O 中比想象中更脆弱
C++20 协程默认使用“无栈”(stackless)模型,co_await 挂起时局部变量存在 coroutine frame 里,不是栈上。但如果你在 await_suspend 回调里捕获异常却没传回协程,co_await 表达式会静默失败,或者触发 std::terminate。
Boost.Asio 用 std::exception_ptr 把 I/O 错误(如 boost::system::error_code)转为异常抛出,所以记得用 try/catch 包裹 co_await 表达式。
- 不要依赖
noexcept—— 即使await_resume()声明为noexcept,底层 I/O 错误仍可能抛异常 - 协程函数返回类型若为
boost::asio::awaitable<t></t>,异常会沿调用链向上抛;若为void且未捕获,直接终止 - 调试时留意
coroutine_handle是否被多次resume()—— I/O 完成回调和超时回调都可能触发,需加状态标记
await_suspend 和回调之间的所有权交接是否干净。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










