C++ 协程-生成器(co_yield 关键字)

co_yield 是 C++20 协程(coroutines)特性中的一个关键字,主要用于实现生成器或惰性求值序列。它解决的核心问题是:如何以简洁、高效、可读的方式编写可以暂停和恢复执行的函数,从而按需产生一系列值。

生成器是一种特殊的函数,它不会一次性计算并返回所有结果,而是能够在每次被请求时产出一个值,然后暂停执行,等到下一次被调用时再从上次暂停的地方继续运行。这种方式非常适合处理大量数据或理论上无限的序列,因为它避免了提前将所有数据加载到内存中,从而显著节省资源。

惰性求值序列则是指一种按需计算的策略:序列中的每个元素只有在真正被使用时才会被计算出来,而不是在序列创建之初就全部生成。这种延迟计算的方式不仅提高了效率,还使得处理超大甚至无限的数据流成为可能。在 C++20 中,通过 co_yield 实现的协程正是构建生成器和惰性求值序列的现代工具,让开发者能以清晰、线性的代码风格写出高效、低内存开销的数据生产逻辑。

1. 问题场景

#include <iostream>


int fibonacci() 
{
    static int prev1 = 0;
    static int prev2 = 1;
    
    int current = prev1;
    int next = prev1 + prev2;

    prev1 = prev2;
    prev2 = next;

    return current;
}

void demo()
{
    int val = fibonacci();
    std::cout << val << std::endl;

    val = fibonacci();
    std::cout << val << std::endl;

    val = fibonacci();
    std::cout << val << std::endl;

    val = fibonacci();
    std::cout << val << std::endl;

    val = fibonacci();
    std::cout << val << std::endl;

    val = fibonacci();
    std::cout << val << std::endl;

    val = fibonacci();
    std::cout << val << std::endl;
}


int main()
{
    demo();
	return 0;
}

2. 解决方法

#include <iostream>
#include <coroutine>

struct Generator
{
    // 协程执行规则定义
    struct promise_type
    {
        int value;

        // 创建协程操作管理对象
        Generator get_return_object()
        {
            return Generator{ std::coroutine_handle<promise_type>::from_promise(*this) };
        }

        // 决定协程创建后是否马上挂起
        std::suspend_always initial_suspend()
        {
            return {};
        }

        // 最终挂起:执行完后不立即销毁
        // std::suspend_never:不挂起,立即销毁资源
        // std::suspend_always:挂起,不立即销毁,等待外部 destroy 释放资源
        std::suspend_never final_suspend() noexcept
        {
            return {};
        }

        // 核心:处理co_yield(暂停并保存值)
        // std::suspend_never:不挂起
        // std::suspend_always:挂起
        std::suspend_always yield_value(int value)
        {
            this->value = value;
            return {};
        }

        // 协程结束时的动作
        void return_void()
        {
            std::cout << "协程结束" << std::endl;
        }

        // 异常未处理时的动作
        void unhandled_exception()
        {
            std::terminate();
        }
    };

    // 协程实际操作执行
    std::coroutine_handle<promise_type> handle;
    explicit Generator(std::coroutine_handle<promise_type> handle)
    {
        this->handle = handle;
    }

    // 获取下一个值(返回是否有值)
    bool next()
    {
        // 协程的执行状态:
        // true: 协程已完成,无法再通过 resume() 恢复执行
        // false: 协程处于挂起态,可以通过 resume() 恢复执行
        if (handle.done())
        {
            return false;
        }
        // 恢复协程执行
        handle.resume();

        // 返回协程状态
        return !handle.done();
    }

    // 获取当前值
    int value() const
    {
        return handle.promise().value;
    }

    ~Generator()
    {
        if (handle)
        {
            handle.destroy();
        }
    }
};

Generator fibonacci()
{
    int prev1 = 0;
    int prev2 = 1;
    
    while (true)
    {
        int current = prev1;
        int next = prev1 + prev2;

        prev1 = prev2;
        prev2 = next;

        co_yield current;
    }
}

void demo()
{
    // 创建协程
    auto gen = fibonacci();

    for (int i = 0; i < 10; ++i)
    {
        // 恢复协程
        bool flag = gen.next();

        if (!flag)
        {
            break;
        }

        // 获得数值
        std::cout << gen.value() << std::endl;
    }
}

int main()
{
    demo();
	return 0;
}

3. 细节探讨

未经允许不得转载:一亩三分地 » C++ 协程-生成器(co_yield 关键字)
评论 (0)

5 + 9 =