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

相关推荐
人道领域2 小时前
Day | 11 【苍穹外卖统计业务的实现:含详细思路分析】
java·数据库·后端·苍穹外卖
xiaoye37087 小时前
Java 自动装箱 / 拆箱 原理详解
java·开发语言
YDS8298 小时前
黑马点评 —— 分布式锁详解加源码剖析
java·spring boot·redis·分布式
迷藏4948 小时前
**发散创新:基于 Rust的开源权限管理系统设计与实战**在现代软件架构中,**权限控制**早已不
java·开发语言·rust·开源
升鲜宝供应链及收银系统源代码服务9 小时前
《IntelliJ + Claude Code + Gemini + ChatGPT 实战配置手册升鲜宝》
java·前端·数据库·chatgpt·供应链系统·生鲜配送
daidaidaiyu9 小时前
Nacos实例一则及其源码环境搭建
java·spring
2301_818419019 小时前
C++中的解释器模式变体
开发语言·c++·算法
爱学习的大牛1239 小时前
windows tcpview 类似功能 c++
c++
小江的记录本9 小时前
【Redis】Redis全方位知识体系(附《Redis常用命令速查表(完整版)》)
java·数据库·redis·后端·python·spring·缓存
摇滚侠9 小时前
Java 项目《谷粒商城-1》架构师级Java 项目实战,对标阿里 P6-P7,全网最强,实操版本
java·开发语言