C++挂起协程的coroutine_handle是否必须在程序结束前主动销毁?
问题描述
请看如下简化示例代码:
std::coroutine_handle<> logger; const char* next_msg = nullptr; void log(const char* msg) { next_msg = msg; if (logger) logger.resume(); } struct wait_msg { bool await_ready() { return next_msg != nullptr; } void await_suspend(std::coroutine_handle<> h) { logger = h; } auto await_resume() { const char* msg = next_msg; next_msg = nullptr; return msg; } }; struct procedure { struct promise_type; using handle_type = std::coroutine_handle<promise_type>; handle_type handle; struct promise_type { procedure get_return_object() { return { handle_type::from_promise(*this)}; } std::suspend_never initial_suspend() { return {}; } std::suspend_never final_suspend() noexcept { return {}; } void return_void() {} void unhandled_exception() { std::terminate(); } }; }; procedure log_messages() { int c = 1; for (;;) { const char* msg = co_await wait_msg{}; std::cout << c++ << ": " << msg << std::endl; } }
假设协程初始和最终阶段都不会挂起,当前协程在co_await处处于挂起状态,请问该协程的coroutine_handle是否必须在程序结束前销毁?
换而言之:main函数中的p.handle.destroy();代码行是否可以删除?
int main() { log("Hello World!"); auto p = log_messages(); log("Hello World, again!"); log("Hello World, and again!"); // 下一行代码是否必要? p.handle.destroy(); }
回答
结论
p.handle.destroy() 不可以随意删除,从C++规范的资源管理要求来看属于必须调用的操作,仅当你明确接受泄漏后果且不需要清理协程栈资源的极端场景下才可省略。
原因说明
- C++协程的状态内存不会自动回收,只有协程正常执行到结束流程时才会触发自动清理逻辑。你示例中的
log_messages是无限循环结构,永远不可能走到协程返回的逻辑,也就永远不会触发自动清理。 - 协程挂起时,协程栈上的所有对象都不会执行析构函数。当前示例里只有
int c这类平凡类型,就算不销毁也只是泄漏一点内存,程序退出时操作系统会回收;但如果后续协程里加入了std::string、文件句柄、网络连接这类非平凡资源,就会产生持续的资源泄漏,甚至引发业务异常。 - 你定义的
promise_type中final_suspend返回std::suspend_never,这个规则仅适用于协程正常执行结束的场景:如果协程能正常跑完,返回std::suspend_never会自动释放协程状态,不需要手动调用destroy,但这个规则对你的无限循环协程完全不生效。 - 就算当前场景下不调用
destroy也不会有可见问题,这种写法也不符合RAII资源主动管理的编码规范,后续如果重构把协程的生命周期放到程序运行过程中(而非main函数退出前),就会产生累积的内存泄漏,埋下线上隐患。
内容的提问来源于stack exchange,提问作者PiotrNycz
相关产品推荐
相关产品推荐

