C++,什么是协程?

在这里插入图片描述


1. 协程(Coroutine)简介

协程是一种特殊的函数,可以在执行过程中暂停并在稍后恢复执行。与普通函数不同,协程可以在任意点挂起(yield)并将控制权返回给调用者,同时保留其状态,以便后续恢复执行。协程常用于异步编程、生成器、状态机等场景。

C++20 引入了对协程的原生支持,使得开发者可以更方便地编写协程代码。


2. 协程的核心概念

  1. 挂起(Suspend)

    • 协程可以在执行过程中挂起,将控制权返回给调用者。

    • 挂起时,协程的状态(局部变量、执行位置等)会被保存。

  2. 恢复(Resume)

    • 协程可以从挂起的位置恢复执行,继续运行。
  3. 协程帧(Coroutine Frame)

    • 协程的状态(局部变量、挂起点等)存储在协程帧中。

    • 协程帧在堆上分配,生命周期由协程库管理。

  4. 协程句柄(Coroutine Handle)

    • 用于控制协程的执行(挂起、恢复、销毁等)。

3. C++20 协程的关键组件

  1. co_await:

    • 用于挂起协程,等待某个操作完成。

    • 操作可以是异步 I/O、定时器、任务等。

  2. co_yield:

    • 用于生成值并挂起协程。

    • 常用于实现生成器(Generator)。

  3. co_return:

    • 用于从协程返回值并结束协程。
  4. 协程类型:

    • 协程函数必须返回一个符合协程接口的类型(如 std::future、generator 等)。

4. 协程示例

4.1 示例 1:简单的协程(生成器)

以下示例实现了一个生成器,用于生成一系列整数。

#include <iostream>
#include <coroutine>

// 定义一个生成器类型
template<typename T>
struct Generator {
    struct promise_type {
        T value; // 生成的值
        Generator get_return_object() { return Generator{std::coroutine_handle<promise_type>::from_promise(*this)}; }
        std::suspend_always initial_suspend() { return {}; } // 初始挂起
        std::suspend_always final_suspend() noexcept { return {}; } // 最终挂起
        void unhandled_exception() { std::terminate(); } // 异常处理
        std::suspend_always yield_value(T val) { value = val; return {}; } // 挂起并返回值
        void return_void() {} // 协程结束
    };

    std::coroutine_handle<promise_type> handle;

    explicit Generator(std::coroutine_handle<promise_type> h) : handle(h) {}
    ~Generator() { if (handle) handle.destroy(); } // 销毁协程

    T next() {
        handle.resume(); // 恢复协程
        return handle.promise().value; // 返回生成的值
    }
};

// 定义一个生成器协程
Generator<int> range(int start, int end) {
    for (int i = start; i <= end; ++i) {
        co_yield i; // 生成值并挂起
    }
}

int main() {
    auto gen = range(1, 5); // 创建生成器
    for (int i = 0; i < 5; ++i) {
        std::cout << gen.next() << std::endl; // 获取生成的值
    }
    return 0;
}

输出:

1
2
3
4
5

4.2 示例 2:异步任务

以下示例实现了一个简单的异步任务,模拟异步操作。

#include <iostream>
#include <coroutine>
#include <thread>
#include <chrono>

// 定义一个异步任务类型
struct AsyncTask {
    struct promise_type {
        int value; // 任务的结果
        AsyncTask get_return_object() { return AsyncTask{std::coroutine_handle<promise_type>::from_promise(*this)}; }
        std::suspend_always initial_suspend() { return {}; } // 初始挂起
        std::suspend_always final_suspend() noexcept { return {}; } // 最终挂起
        void unhandled_exception() { std::terminate(); } // 异常处理
        void return_value(int val) { value = val; } // 返回值
    };

    std::coroutine_handle<promise_type> handle;

    explicit AsyncTask(std::coroutine_handle<promise_type> h) : handle(h) {}
    ~AsyncTask() { if (handle) handle.destroy(); } // 销毁协程

    int get() {
        handle.resume(); // 恢复协程
        return handle.promise().value; // 返回结果
    }
};

// 定义一个异步任务协程
AsyncTask computeAsync() {
    std::cout << "Starting async computation..." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
    co_return 42; // 返回结果
}

int main() {
    auto task = computeAsync(); // 创建异步任务
    std::cout << "Waiting for result..." << std::endl;
    int result = task.get(); // 获取结果
    std::cout << "Result: " << result << std::endl;
    return 0;
}

输出:

Starting async computation...
Waiting for result...
Result: 42

5. 协程的优势

  1. 简化异步编程:

    • 协程可以将异步代码写成同步风格,避免回调地狱。
  2. 高效的状态管理:

    • 协程的状态由编译器自动管理,无需手动维护状态机。
  3. 灵活的控制流:

    • 协程可以挂起和恢复,适用于生成器、流处理等场景。

6. 总结

  • 协程是一种可以挂起和恢复的函数,适用于异步编程、生成器等场景。

  • C++20 引入了原生协程支持,通过 co_await、co_yield 和 co_return 实现协程逻辑。

  • 协程的核心是协程帧和协程句柄,用于保存状态和控制执行。

  • 通过生成器和异步任务的示例,可以更好地理解协程的用法和优势。

参考

【1】到底该怎么理解协程?
【2】[C++] 协程初步:手把手教你写一个协程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智驾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值