You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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;
}

关键说明

  1. 协程工厂传递:forever接收的是协程工厂(如lambda)而非已启动的协程句柄,因为单个协程句柄只能被执行一次,每次循环都需要生成新的子协程实例。
  2. 完成回调机制:通过修改协程promise_type的final_suspend逻辑,让子协程结束时自动唤醒forever协程,实现循环重启。
  3. awaiter的挂起逻辑:callee_awaiter在挂起父协程前启动子协程,确保子协程执行流程正常推进,直到其完成后再恢复父协程的循环。

内容的提问来源于stack exchange,提问作者Gene

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 18:23:14