C++协程

简单的协程

cpp 复制代码
#include <iostream>   // 包含标准输入输出流头文件
#include <thread>     // 包含线程支持头文件
#include <coroutine>  // 包含协程支持头文件
#include <future>     // 包含异步操作支持头文件
#include <chrono>     // 包含时间库头文件
#include <functional> // 包含函数对象头文件

// 定义一个名为 Result 的结构体
struct Result{
    // 定义一个 promise_type 结构体
    struct promise_type {
        // 返回一个 Result 对象
        Result get_return_object() { return {}; }
        // 定义协程的初始挂起点
        std::suspend_never initial_suspend() { return {}; }
        // 定义协程的最终挂起点
        std::suspend_never final_suspend() noexcept { return {}; }
        // 协程的返回值类型为 void
        void return_void() {}
        // 未处理的异常处理函数
        void unhandled_exception() {}
    };
};

// 定义一个全局的协程句柄
std::coroutine_handle<> coroutine_handle;

// 定义一个可等待对象结构体
struct AWaitableObject
{
    // 默认构造函数
    AWaitableObject() {}

    // 定义 await_ready 函数,表示是否准备好
    bool await_ready() const { return false; }

    // 定义 await_resume 函数,表示恢复时的返回值
    int await_resume() { return 0; }

    // 定义 await_suspend 函数,表示挂起协程时的操作
    void await_suspend(std::coroutine_handle<> handle){
        // 保存协程句柄
        coroutine_handle = handle;
    }
};

// 定义一个协程函数
Result CoroutineFunction()
{
    // 打印开始协程的信息
    std::cout << "start coroutine\n";
    // 等待可等待对象
    int ret = co_await AWaitableObject(); 
    // 打印完成协程的信息
    std::cout << "finish coroutine\n";
}

// 主函数
int main()
{
    // 打印开始的信息
    std::cout << "start \n"; 
    // 调用协程函数
    auto coro = CoroutineFunction();
    // 打印协程 co_await 的信息
    std::cout << "coroutine co_await\n"; 
    // 恢复协程
    coroutine_handle.resume();

    // 返回 0 表示程序正常结束
    return 0;
}

co_await 是 C++20 引入的一种新的操作符,用于支持协程的异步等待。co_await 操作符的使用需要等待一个可等待对象,该对象需要实现三个特定的成员函数或通过重载对应的操作符。这三个成员函数分别是 await_ready(), await_suspend(), 和 await_resume()。我们来看一下在你的代码中 co_await AWaitableObject() 是如何工作的:

  1. await_ready:

    cpp 复制代码
    bool await_ready() const { return false; }
    • 这个函数返回一个布尔值,表示这个对象是否已经准备好。如果返回 true,协程将不会被挂起,直接继续执行后续代码。如果返回 false,协程将被挂起,直到这个对象准备好。
    • 在你的代码中,await_ready 总是返回 false,所以协程会被挂起。
  2. await_suspend:

    cpp 复制代码
    void await_suspend(std::coroutine_handle<> handle) {
        coroutine_handle = handle;
    }
    • 这个函数在协程被挂起时被调用。它接受一个 std::coroutine_handle 作为参数,表示当前协程的句柄。
    • 在你的代码中,它将协程句柄保存到全局变量 coroutine_handle 中。这允许你在以后手动恢复这个协程。
  3. await_resume:

    cpp 复制代码
    int await_resume() { return 0; }
    • 这个函数在协程恢复时被调用。它的返回值将成为 co_await 表达式的结果。
    • 在你的代码中,它返回 0

现在我们具体来看 co_await AWaitableObject(); 在你的代码中的执行流程:

  1. 进入 CoroutineFunction 函数:

    cpp 复制代码
    std::cout << "start coroutine\n";
    • 打印 "start coroutine"。
  2. 执行 co_await AWaitableObject():

    • 创建一个 AWaitableObject 对象。
    • 调用 AWaitableObjectawait_ready 方法,返回 false,因此协程挂起。
    • 调用 AWaitableObjectawait_suspend 方法,将协程句柄保存到全局变量 coroutine_handle 中。
    • 协程挂起,控制权返回到调用方 main 函数。
  3. 回到 main 函数:

    cpp 复制代码
    std::cout << "coroutine co_await\n";
    • 打印 "coroutine co_await"。
  4. 恢复协程:

    cpp 复制代码
    coroutine_handle.resume();
    • 手动恢复协程,调用 await_resume 方法,返回 0
  5. 继续执行 CoroutineFunction:

    cpp 复制代码
    std::cout << "finish coroutine\n";
    • 打印 "finish coroutine"。

整个过程展示了协程的挂起和恢复机制,通过 co_await 实现异步操作。AWaitableObject 作为一个可等待对象,实现了协程所需的三个成员函数,协调了协程的挂起和恢复。

多个任务

cpp 复制代码
#include <chrono>
#include <coroutine>
#include <deque>
#include <iostream>
#include <windows.h>
std::deque<std::coroutine_handle<>> tasks;
// 简单的协程类
struct AsyncTask {
    struct promise_type {
        AsyncTask get_return_object() { return {}; }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() {}
    };
};
struct AWaitableObject {
    int delay;
    AWaitableObject(int delay) : delay(delay) {}
    bool await_ready() const { return false; }
    int await_resume() { return 0; }
    void await_suspend(std::coroutine_handle<> handle) {
        tasks.push_back(handle);
        Sleep(delay);
    }
};
// 模拟异步任务1
AsyncTask task1() {
    co_await AWaitableObject(200);
    std::cout << "Task 1 completed" << std::endl;
}

// 模拟异步任务2
AsyncTask task2() {
    co_await AWaitableObject(100);
    std::cout << "Task 2 completed" << std::endl;
}

// 主函数
int main() {
    // 打印开始的信息
    std::cout << "main start \n";
    task1();
    task2();
    std::cout << "main continue\n";
    // 恢复协程
    while (!tasks.empty()) {
        auto task = tasks.front();
        tasks.pop_front();
        task.resume();
    }
    std::cout << "main completed" << std::endl;
    return 0;
}

推荐资料:
深入浅出c++协程

相关推荐
听忆.3 分钟前
RabbitMQ消息可靠性等机制详解(精细版三)
java·开发语言·spring boot·后端·spring·java-ee·rabbitmq
泡芙冰淇淋ya13 分钟前
【Spring Boot】spring boot环境搭建
java·spring boot·后端
追风筝的Coder15 分钟前
泛微开发修炼之旅--29用计划任务定时发送邮件提醒
java
欣慰的三叶草(● ̄(エ) ̄●)17 分钟前
01--SpringAI接入大模型,chatgpt,Java接入人工智能大模型
java·人工智能·chatgpt
岑梓铭29 分钟前
后端之路——阿里云OSS云存储
java·spring boot·阿里云·阿里云oss
vx_Biye_Design34 分钟前
驾校管理系统-计算机毕业设计源码49777
java·css·vue.js·spring boot·mysql·ajax·mvc
爱吃香菜¹43 分钟前
深入理解【 String类】
java·开发语言
六月的雨__1 小时前
跑腿平台小程序的设计
java·sql·学习·小程序
u0104058361 小时前
实现Java Web应用的高性能负载均衡方案
java·前端·负载均衡