协程函数必须返回task或future等可挂起类型,否则co_await会编译失败;需自定义promise type和awaiter,推荐用cppcoro::task或asio::use_awaitable封装异步io。

协程函数必须返回 task 或 future 类型才能挂起
直接在普通函数里用 co_await 会编译失败,因为 C++20 协程要求函数有符合约定的返回类型,比如自定义的 task<:string></:string> 或 std::experimental::coroutine_handle 封装类型。标准库没提供现成的 task,得自己实现或用第三方(如 cppcoro、libunifex)。不定义好 promise type 和 awaiter,co_await 就不认识你传进去的对象。
- 常见错误:
error: 'co_await' is not allowed in a function that does not return a coroutine type - 推荐做法:用
cppcoro::task<t></t>(需#include <cppcoro></cppcoro>),它已实现标准 promise 接口 - 不要试图用
std::future直接返回——它不满足可挂起条件;std::future是被动等待,不是协程感知类型
co_await 的对象必须实现 await_ready/await_suspend/await_resume
HTTP 请求本身是 IO 操作,不能直接 co_await 一个 curl_easy_perform 调用。你需要把底层异步 IO 封装成 awaitable 对象。比如 libcurl 支持 multi interface + socket 事件驱动,但它的回调机制和协程不兼容,得靠 event loop(如基于 epoll 或 IOCP)桥接。
- 典型场景:封装
curl_multi_socket_action触发的 socket 可读/可写事件,让协程在 fd 就绪时恢复 - 关键点:
await_suspend里把当前协程 handle 注册到事件循环,而不是阻塞等待 - 容易踩坑:在
await_resume里直接返回 curl 返回码——实际结果可能还没就绪,要等事件循环回调后才真正完成
用 asio::use_awaitable 是最简可行路径
如果你不硬要绑死 libcurl,asio(1.72+)对 C++20 协程支持成熟,asio::ip::tcp::socket::async_read_some 等函数接受 asio::use_awaitable 标签后,就能直接 co_await。HTTP 解析仍需手动处理(请求行、header、body),但网络层完全无感。
- 示例片段:
asio::awaitable<:string> http_get(asio::ip::tcp::socket& sock, const std::string& host, const std::string& path) { co_await sock.async_connect( asio::ip::tcp::endpoint{asio::ip::make_address(host), 80}, asio::use_awaitable ); std::string req = "GET " + path + " HTTP/1.1\r\nHost: " + host + "\r\nConnection: close\r\n\r\n"; co_await asio::async_write(sock, asio::buffer(req), asio::use_awaitable); // ... 读响应 }</:string> - 注意:
asio::use_awaitable要求 io_context 在运行中,且协程启动前调用co_spawn(io_ctx, ...),否则会崩溃 - 性能影响:每次
co_await都涉及一次堆分配(promise 对象),高频请求建议复用协程栈或使用 stackful 协程(如cppcoro::static_thread_pool)
别忽略 DNS 解析的同步阻塞问题
asio::ip::tcp::resolver::async_resolve 支持 use_awaitable,但默认 resolver 用的是系统 getaddrinfo,某些 libc 实现(如 musl)下仍是同步调用。在高并发场景下,这会导致线程卡住,协程调度器被拖慢。
- 解决办法:用 asio 内置的线程池 resolver(
asio::ip::tcp::resolver::executor_type绑定到独立线程池) - 或者换用纯异步 DNS 库(如 c-ares),再封装成 awaitable
- 验证方式:抓取 strace,看是否有
getaddrinfo系统调用出现在协程线程中
co_await 自动搞定。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











