c++20协程

cpp 复制代码
1.协程是一种可以挂起和恢复的函数,适用于异步编程、生成器等场景。
2.一个函数如果包含以下关键字之一,即为协程函数:
    2.1 co_await:用于暂停协程的执行,直到一个等待的操作完成
        2.1.1 用法auto ret =  co_await async_fun();//async_fun为awaitable等待对象
        2.1.2 awaitable对象,它需要有3个成员函数:
              bool await_ready():检查操作是否完成(为false,表示需要暂停)
              {下面3个中的一个:
				void await_suspend(std::coroutine_handle<> handle):处理协程挂起逻辑,启动异步逻辑,通常会保存协程句柄以便后续恢复
				bool await_suspend(std::coroutine_handle<> handle):返回bool类型的
				std::coroutine_handle<> handle await_suspend(std::coroutine_handle<> handle):返回handle类型的
              }
			  T await_resume():恢复时调用,返回等待结果(T为结果类型)
        2.1.3 默认的两个awaitable对象std::suspend_always、std::suspend_never

    2.2 co_yield:用于生成一个值,然后暂停协程
        语法:co_yield value; // 等价于 co_await promise.yield_value(value)

    2.3 co_return:返回结果并结束协程
        co_return expression; //用于返回最终结果并终止协程,结果会通过promise_type传递给调用者

3.协程函数必须返回一个符合协程接口的类型
    3.1 这个类型通常是一个包含promise_type嵌套结构体的类或结构体
    3.2 这个类型必须是可构造的,以便在get_return_object函数中创建实例
    3.3 这个类型必须是可析构的,以确保在协程结束时能够正确清理资源
    3.4 这个类型必须提供方法来控制协程的执行,如提供promise_type成员变量handle

4.promise_type结构体的要求(负责协程从创建到结束的整个生命周期管理)
        4.1 必须包含一个get_return_object函数,该函数返回一个代表协程的对象
        4.2 必须包含一个initial_suspend函数,该函数返回一个可等待对象,用于决定协程在创建后是否立即挂起
        4.3 必须包含一个final_suspend函数,该函数返回一个可等待对象,用于决定协程在执行完co_return后是否挂起
        4.4 必须包含一个return_void(协程函数返回void)或return_value(协程函数有返回值非void)函数
        4.5 必须包含一个unhandled_exception函数,当协程内部抛出未捕获的异常时,该函数会被调用,用于处理异常
        4.6 yield_value函数,配合co_yield语句,使协程能够暂停执行并向调用者返回值,同时保持协程的状态以便后续恢复执行。
            每次co_yield都会触发yield_value函数的调用,从而实现一系列值的生成和传递
for:
    struct TASK { //定义一个协程接口的类型
        struct promise_type { //包含promise_type嵌套结构体
            TASK get_return_object() { ... }
            std::suspend_always initial_suspend() { ... }
            std::suspend_always final_suspend() { ... }
            void unhandled_exception() { ... }
            std::suspend_always yield_value(T val) { ... }
            void return_void() {}
        };
        std::coroutine_handle<promise_type> handle;//用于外部控制协程(恢复、销毁)的轻量级指针
        TASK(...){...}
        ~TASK() {...} 
    };
    TASK coroutineFun() { //协程函数
        std::this_thread::sleep_for(std::chrono::seconds(200));
        co_return ;
    }

co_await流程测试:

cpp 复制代码
static void _sleep()
{
    std::cout << "sleep 200" << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(200)); //
}

struct Task {
    struct promise_type;
    using handle_type = std::coroutine_handle<promise_type>;
    // 承诺对象:协程内部状态管理者
    struct promise_type {
        //这个函数负责返回一个代表协程的对象
        //这个返回的对象包含了协程的句柄等信息,用于外部对协程进行控制,比如恢复协程执行、检查协程状态等。它是协程与外部世界交互的重要桥梁
        Task get_return_object() {
            std::cout << "get_return_object" << std::endl;
            _sleep();
            return Task(handle_type::from_promise(*this));
        }
        //首次执行前是否立即挂起,它返回一个类型为std::suspend_always或std::suspend_never的对象
        //如果返回std::suspend_always,协程在创建后会立即挂起,直到被显式恢复;
        //如果返回std::suspend_never,协程会立即开始执行其函数体
        std::suspend_never initial_suspend() { 
            std::cout << "initial_suspend" << std::endl;
            _sleep();
            return {}; 
        }
        //该函数决定协程在执行完co_return后,是否挂起。同样返回std::suspend_always或std::suspend_never类型的对象
        //返回std::suspend_always意味着协程在co_return后会挂起,此时可以进行一些清理工作(如果需要),然后再由外部决定是否销毁协程;
        //返回std::suspend_never则表示协程执行完co_return后直接结束,其资源会被立即清理
        std::suspend_always final_suspend() noexcept { 
            std::cout << "final_suspend" << std::endl;
            _sleep();
            return {}; 
        }
        // 它的主要作用是处理 co_yield 表达式产生的值,返回awaitable类型等待对象
        //当协程执行到 co_yield 语句时,yield_value 函数会被调用,负责存储或处理这个通过 co_yield 返回的值,以便外部调用者能够获取
        std::suspend_always yield_value(int value) {
            current_value = value;
            return {};
        }
        // void return_void() noexcept  {
        //     std::cout << "return_void" << std::endl;
        //     _sleep();
        // }
        // 处理co_return:保存返回值
        void return_value(int v) { 
            std::cout << "return_value=" << v << std::endl;
            value = v; 
        }

        void unhandled_exception() {
            std::cout << "unhandled_exception" << std::endl;
            std::terminate();
        }
        int current_value =222; // 存储协程返回值
    };
    
    handle_type h;
    Task(handle_type h) : h(h) {
        std::cout << "construct Task" << std::endl;
        _sleep();
    }
    ~Task() {
        std::cout << "destroy Task" << std::endl;
        _sleep();
         if (h) h.destroy(); 
        }
};

struct awaitFun {
    //检查操作是否完成(初始为false,需要暂停,所以执行await_suspend)
    bool await_ready() const noexcept { 
        std::cout << "await_ready,done=" << done << std::endl;
        return done; 
    }
    // 处理协程挂起逻辑,启动异步逻辑
    void await_suspend(std::coroutine_handle<> h) noexcept {
        std::cout << "await_suspend started" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(2));// 模拟异步操作,这里使用线程睡眠
        result = 123;
        done = true;
        std::cout << "await_suspend end,ret=" << result << std::endl;
        h.resume(); //操作完成,恢复协程
    }
    int await_resume() const noexcept { 
        std::cout << "await_resume,ret=" << result << std::endl;
        return result; 
    } //恢复时调用,返回等待结果(T为结果类型)
    bool done = false;
    int result = 0;
};

Task testAwait() {
    std::cout << "testAwait start" << std::endl;
    int ret = co_await  awaitFun();
    std::cout << "testAwait end,ret=" << ret << std::endl;
}

int main() {
    Task t = testAwait();//调用testAwait()时,首先调用get_return_object->接着调用initial_suspend->返回std::suspend_always暂停执行
    std::cout << "Result: " << t.h.promise().value << std::endl; // 输出8
    std::cout << "resume end" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(6));
    return 0;
}
输出:
get_return_object
sleep 200
construct Task
sleep 200
initial_suspend        (其返回suspend_never,所以才能继续往下执行,进入协程函数)
sleep 200
testAwait start
await_ready,done=0     (await_ready返回false,才执行await_suspend)
await_suspend started
await_suspend end,ret=123
await_resume,ret=123   (调用resume恢复协程,继续往下执行)
testAwait end,ret=123
final_suspend
sleep 200
Result: 222
resume end
destroy Task
sleep 200

用co_yield产生序列:

cpp 复制代码
#include <iostream>
#include <coroutine>
#include <vector>

// 生成器返回类型
struct FibonacciGenerator {
    struct promise_type;
    using handle_type = std::coroutine_handle<promise_type>;
    handle_type h;
    FibonacciGenerator(handle_type h) : h(h) {}
    ~FibonacciGenerator() { if (h) h.destroy(); }

    struct promise_type {
        FibonacciGenerator get_return_object() {
            return FibonacciGenerator(handle_type::from_promise(*this));
        }
        std::suspend_always initial_suspend() { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() {}
        // 用于存储生成的值
        int current_value;
        // 修正后的yield_value函数,接受int类型参数
        std::suspend_always yield_value(int value) {
            current_value = value;
            return {};
        }
    };

    bool move_next() {
        if (h.done()) return false;
        h.resume();
        return!h.done();
    }

    int current() {
        return h.promise().current_value;
    }
};

// 协程函数
FibonacciGenerator fibonacci_generator(int limit) {
    int a = 0;
    while (a < limit) {
        co_yield a++;
    }
}

int main() {
    FibonacciGenerator gen = fibonacci_generator(10);
    while (gen.move_next()) {
        std::cout << gen.current() << " ";
    }
    std::cout << std::endl;
    return 0;
}
输出:0 1 2 3 4 5 6 7 8 9

协程文件读取:

cpp 复制代码
#include <iostream>
#include <coroutine>
#include <thread>
#include <string>
#include <fstream>

class FileReader{
public:
    struct promise_type;
    using handle_type  = std::coroutine_handle<promise_type>;
    handle_type h_;
    FileReader(handle_type h):h_(h)
    {

    }
    ~FileReader()
    {
        if(h_)
            h_.destroy();
    }
    struct promise_type
    {
        FileReader get_return_object() {
            return FileReader(handle_type::from_promise(*this));
        }
        std::suspend_always initial_suspend() { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() {}
        // 用于存储生成的值
        std::string curLine_;
        // yield_value 函数处理 co_yield 产生的值
        std::suspend_always yield_value(std::string &value) {
            curLine_ = value;
            return {};
        }
    };

    bool next_line()
    {
        //执行co_return后,h_.done()将返回true
        if (h_.done()) return false;
        h_.resume();
        return!h_.done();
    }

    std::string line(){
        return h_.promise().curLine_;
    }
};

bool filterOne(const std::string & line)
{
    return line.find("error") != std::string::npos;
}

// 协程函数:按行读取文件并过滤
FileReader readFileAndFilter(const std::string& filename) {
    std::ifstream file(filename);
    if (!file.is_open()) {
        std::cerr << "无法打开文件: " << filename << std::endl;
        co_return;
    }
    std::string line;
    while (std::getline(file, line)) {
        if (!filterOne(line)) {
            co_yield line;
        }
    }
    file.close();
    co_return;//执行 co_return 后,FileReader.h.done() 将返回 true
}
int main() 
{
    FileReader reader = readFileAndFilter("./example.txt");
    while (reader.next_line()) {
        std::cout << reader.line() << std::endl;
    }
    return 0;
}
相关推荐
崔庆才丨静觅2 分钟前
hCaptcha 验证码图像识别 API 对接教程
前端
曹牧44 分钟前
Spring Boot:如何测试Java Controller中的POST请求?
java·开发语言
passerby60611 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了1 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅1 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅1 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
爬山算法2 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
kfyty7252 小时前
集成 spring-ai 2.x 实践中遇到的一些问题及解决方案
java·人工智能·spring-ai
猫头虎2 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven
李少兄2 小时前
在 IntelliJ IDEA 中修改 Git 远程仓库地址
java·git·intellij-idea