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++协程

相关推荐
剑锋所指,所向披靡!35 分钟前
C++之类模版
java·jvm·c++
Coder_Boy_1 小时前
基于SpringAI的在线考试系统-0到1全流程研发:DDD、TDD与CICD协同实践
java·人工智能·spring boot·架构·ddd·tdd
sheji34161 小时前
【开题答辩全过程】以 面向高校校园的物物交换系统设计与实现为例,包含答辩的问题和答案
java·eclipse
鱼跃鹰飞1 小时前
Leetcode347:前K个高频元素
数据结构·算法·leetcode·面试
卓怡学长1 小时前
m115乐购游戏商城系统
java·前端·数据库·spring boot·spring·游戏
2501_944526421 小时前
Flutter for OpenHarmony 万能游戏库App实战 - 蜘蛛纸牌游戏实现
android·java·python·flutter·游戏
C+-C资深大佬1 小时前
C++风格的命名转换
开发语言·c++
No0d1es1 小时前
2025年粤港澳青少年信息学创新大赛 C++小学组复赛真题
开发语言·c++
点云SLAM2 小时前
C++内存泄漏检测之手动记录法(Manual Memory Tracking)
开发语言·c++·策略模式·内存泄漏检测·c++实战·new / delete
好评1242 小时前
【C++】二叉搜索树(BST):从原理到实现
数据结构·c++·二叉树·二叉搜索树