纯标准库+编译器原生协程无法直接发http请求,必须桥接底层i/o:io_uring(linux 5.1+)、epoll+非阻塞socket或libcurl multi接口;std::coroutine_handle仅调度执行流,不处理网络;常见卡死或报错源于awaiter未注册事件或未启动轮询。

纯标准库 + 编译器原生协程,无法直接发出 HTTP 请求。你必须桥接底层 I/O 能力——要么用 io_uring(Linux 5.1+),要么用 epoll + 非阻塞 socket,要么用 libcurl 的 multi 接口驱动。没有“零依赖又能发请求”的捷径。
为什么 std::coroutine_handle 本身不能发网络请求
std::coroutine_handle 只负责挂起和恢复函数执行流,它不碰 socket、不调系统调用、不管理事件循环。你写一个 co_await 表达式,编译器只生成状态机跳转代码;真正让请求飞出去的,是 awaiter 里写的 epoll_ctl、io_uring_submit 或 curl_multi_add_handle。
常见错误现象:co_await some_http_task() 卡死不动,或抛 std::system_error: Operation not permitted——大概率是 awaiter 的 await_suspend 没注册事件,或者根本没启动轮询线程。
- 别指望
std::experimental::coroutine(已废弃)或裸promise_type实现网络 I/O - 别在
await_ready()里调connect()或send():阻塞操作会拖垮整个协程线程 - 所有非阻塞 socket 必须提前设
fcntl(fd, F_SETFL, O_NONBLOCK),否则read/write仍可能阻塞
用 epoll + 非阻塞 socket 封装最简 awaiter
这是 Linux 下最轻量、无第三方依赖的路径。核心是把 socket fd 和 std::coroutine_handle 绑定到 epoll 实例上,靠 epoll_wait 触发恢复。
关键点:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
await_ready()必须做一次真实read或connect:内核缓冲区可能已有数据,不能只等 epoll 通知 -
await_suspend()中调epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev),并把h存进全局std::unordered_map<int std::coroutine_handle>></int>(单线程场景可裸用,无需锁) - 必须有一个独立线程或主循环反复调
epoll_wait,遍历就绪事件,取出h并调h.resume() - socket 错误(如
connect失败)需在await_resume()中检查getsockopt(fd, SOL_SOCKET, SO_ERROR, ...),否则静默失败
用 libcurl multi 接口避免阻塞,但别碰 easy_perform
libcurl 是跨平台且轻量(比 Boost.Asio 小得多),但它默认的 curl_easy_perform 是同步阻塞的——直接 co_await 它等于白用协程。
正确做法是用 curl_multi_* 系列 API,在事件循环中驱动:
- 创建
CURLM *句柄,设CURLOPT_NOSIGNAL=1L防 SIGPIPE - 每个请求用
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, ...)捕获响应体,用curl_multi_add_handle加入 multi 句柄 - 在
await_suspend里不立即发起请求,而是把std::coroutine_handle存进请求上下文,交由外部轮询线程调curl_multi_perform和curl_multi_info_read - 收到
CURLMSG_DONE后,从上下文中取出h并resume();忘记清理CURL*会导致句柄泄漏 - 务必设
CURLOPT_TIMEOUT_MS,否则超时逻辑失效,协程永久挂起
io_uring 是终极轻量方案,但仅限新内核
如果你目标平台是 Linux 5.11+,io_uring 是目前最接近“标准库级”异步 I/O 的方案:一次 submit 就能发请求、收响应,无需用户态轮询。
实操要点:
- 初始化
io_uring_setup(0, ¶ms)得到 ring,用io_uring_queue_init包装 - awaiter 的
await_suspend调io_uring_prep_recv或io_uring_prep_connect,再io_uring_sqe_set_data(sqe, h.address()) - 不需要额外线程轮询:用
io_uring_enter等待完成,或搭配IORING_SETUP_IOPOLL由内核主动提交 - 注意
io_uring的内存对齐要求(buffer 必须页对齐)、文件描述符生命周期(fd 关闭后 ring 中 pending 请求会失败) - Windows/macOS 不支持,别在跨平台项目里默认启用
最容易被忽略的是:无论选 epoll、curl multi 还是 io_uring,协程挂起后,对应的资源(socket fd、CURL*、ring buffer)必须存活到 await_resume 执行完——局部变量、临时对象、自动释放的智能指针,都会导致未定义行为。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










