如何实现forever函数,让协程无限循环执行(等效于协程内循环)
问题描述
我有一个仅包含单个co_await等待事件的协程函数,代码如下:
auto coro = [&]()->coroutine { co_await evt; std::cerr << "event was triggered\n"; };
需要编写一个void forever(auto&&)函数,通过forever(coro);调用,让这个协程无限循环执行,效果等同于在协程内部写无限循环的版本:
auto forever_coro = [&]()->coroutine { for(auto counter = 1;;++counter) { co_await evt; std::cerr << "event was triggered " << counter << " time(s)\n"; } };
核心分析
forever()本身必须是协程,它需要在被调用的协程执行完成后恢复执行。大致框架如下:
auto forever(auto&& coro)->coroutine { for(;;) co_await callee_awaiter(coro); }
核心问题是:如何实现这个callee_awaiter?
解决方案
要实现callee_awaiter,需要让它跟踪传入的协程工厂、在子协程完成时唤醒forever协程,并且每次循环都生成新的子协程实例(因为单个协程句柄只能被执行一次)。
步骤1:改造协程返回类型,支持完成回调
首先修改基础的coroutine类型,给其promise_type添加完成回调,让子协程结束时能通知父协程:
#include <coroutine> #include <iostream> #include <functional> struct coroutine { struct promise_type { std::function<void()> on_completion; coroutine get_return_object() { return {}; } std::suspend_never initial_suspend() { return {}; } // 子协程结束时触发回调 std::suspend_always final_suspend() noexcept { if (on_completion) { on_completion(); on_completion = nullptr; } return {}; } void return_void() {} void unhandled_exception() { std::terminate(); } }; };
步骤2:实现callee_awaiter
这个awaiter负责生成子协程、注册完成回调、挂起父协程直到子协程结束:
template<typename CoroFactory> struct callee_awaiter { CoroFactory coro_factory; std::coroutine_handle<> parent_handle; callee_awaiter(CoroFactory&& factory) : coro_factory(std::forward<CoroFactory>(factory)) {} bool await_ready() const noexcept { return false; } void await_suspend(std::coroutine_handle<> parent) { parent_handle = parent; // 生成新的子协程实例 auto child_coro = coro_factory(); auto child_handle = std::coroutine_handle<coroutine::promise_type>::from_promise(child_coro.promise()); // 注册回调:子协程结束时唤醒父协程 child_handle.promise().on_completion = [this]() { parent_handle.resume(); }; // 启动子协程 child_handle.resume(); } void await_resume() noexcept {} };
步骤3:实现forever协程函数
利用callee_awaiter实现无限循环重启子协程:
auto forever(auto&& coro_factory) -> coroutine { for (;;) { co_await callee_awaiter(std::forward<decltype(coro_factory)>(coro_factory)); } }
测试示例
搭配模拟事件对象验证功能:
// 模拟可触发的事件对象 struct event { std::coroutine_handle<> waiter; bool await_ready() const noexcept { return false; } void await_suspend(std::coroutine_handle<> h) noexcept { waiter = h; } void await_resume() noexcept {} void trigger() { if (waiter) { waiter.resume(); waiter = nullptr; } } }; int main() { event evt; auto coro = [&]() -> coroutine { co_await evt; static int counter = 0; std::cerr << "event was triggered " << ++counter << " time(s)\n"; }; // 启动forever协程 forever(coro); // 模拟触发事件3次 evt.trigger(); evt.trigger(); evt.trigger(); return 0; }
关键说明
- 协程工厂传递:
forever接收的是协程工厂(如lambda)而非已启动的协程句柄,因为单个协程句柄只能被执行一次,每次循环都需要生成新的子协程实例。 - 完成回调机制:通过修改协程
promise_type的final_suspend逻辑,让子协程结束时自动唤醒forever协程,实现循环重启。 - awaiter的挂起逻辑:
callee_awaiter在挂起父协程前启动子协程,确保子协程执行流程正常推进,直到其完成后再恢复父协程的循环。
内容的提问来源于stack exchange,提问作者Gene
相关产品推荐
相关产品推荐

