不是必须返回 std::future 或自定义 awaitable,但协程函数返回类型必须嵌套定义 promise_type,并实现 get_return_object()、initial_suspend()、final_suspend() 和 unhandled_exception() 等关键方法,以支持挂起、恢复与异常传播等协程语义。

协程函数必须返回 std::future 或自定义 awaitable 吗?
不是必须,但直接返回 std::future 会丢失协程语义——它只是把异步操作“包装成同步等待”,没真正挂起和恢复。C++20 协程要求返回类型实现 promise_type,且该类型需提供 initial_suspend()、final_suspend()、get_return_object() 和 unhandled_exception()。主流做法是用 boost::asio::awaitable<t></t> 或自己写轻量 task<t></t>,而不是裸 std::future。
常见错误:写了个 auto http_get(std::string url) -> std::future<:string></:string>,然后在协程里 co_await http_get(...) ——这会编译失败,因为 std::future 没定义 operator co_await。
- 若用
boost::asio(推荐),直接返回asio::awaitable<:string></:string>,底层自动适配调度器 - 若用
libcurl+ 自建 event loop,需为CURLhandle 注册CURLOPT_XFERINFOFUNCTION并配合co_await自定义 awaiter - 别试图给
std::future加operator co_await:它不支持暂停/恢复,强行适配会导致线程阻塞或未定义行为
如何让 libcurl 异步调用可被 co_await?
libcurl 默认是阻塞式,要协程化,关键是把它接入事件循环,并将 socket 可读/可写事件转为 suspend/resume 点。不能直接 curl_easy_perform,而要用 curl_multi_* API + curl_multi_wait 或 curl_multi_poll。
典型流程:启动 multi handle → 注册 socket 事件回调(CURLMOPT_SOCKETFUNCTION)→ 在回调中触发 awaiter 的 await_ready() 切换为 false,等事件到来后调用 resume()。
- 每个请求需绑定独立
CURLhandle 和对应awaitable对象,避免状态混淆 -
CURLOPT_WRITEFUNCTION必须设为内存写入(如std::stringbuffer),否则协程恢复时数据可能已被覆盖 - 注意
curl_multi_cleanup不能在协程栈上直接调用,应由 event loop 统一管理生命周期
co_await 一个 HTTP 请求时,异常怎么传播?
协程内抛出异常不会立即终止整个线程,而是被捕获并存入 promise 对象,等 co_await 表达式求值完成时重新抛出。但前提是 promise_type 正确实现了 unhandled_exception()。
常见陷阱:忘了在 task<t>::promise_type::unhandled_exception()</t> 中调用 std::current_exception() 存储异常,导致 co_await 后静默失败或 crash。
- HTTP 层级错误(如 404、500)建议封装为
std::error_code或自定义http_error类型,通过co_return返回,而非抛异常 - 网络层异常(连接超时、DNS 失败)应抛出,由协程调用方用
try/catch捕获,此时必须确保 promise_type 存储了异常对象 - 不要在
await_suspend里捕获并吞掉异常——这会让协程永远挂起
多个并发 HTTP 请求怎么避免 event loop 饿死?
协程本身不解决调度问题;如果所有请求都绑在同一个线程的 event loop 上,而某个请求响应极慢(比如卡在 DNS 查询),其他协程就无法推进——这不是协程缺陷,是 event loop 设计问题。
关键不是“怎么写协程”,而是“谁来驱动它”。boost::asio::io_context 默认单线程,需显式调用 io_context::run_one() 或 io_context::poll() 才能推进;裸用 std::coroutine_handle 更容易卡住。
- 生产环境务必启用多线程
io_context(io_context::post()分发任务),或使用asio::thread_pool - 对超时敏感的请求,应设置
asio::use_awaitable的 timeout 版本,例如socket.async_wait(asio::socket_base::wait_read, asio::use_awaitable)配合asio::steady_timer - 别依赖
co_yield让出控制权来“调度”——它不触发 event loop 运行,只是挂起当前协程
协程简化的是异步代码的线性表达,不是自动带来并发能力;event loop 的吞吐和公平性,得靠调度策略和资源隔离来保障,这点很容易被忽略。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











