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 小时前
现如今的AI IDE:提示词策略与MCP Server使用感悟
前端·人工智能·mcp
徐同保2 小时前
Electron创建demo项目和打包
前端·javascript·electron
用户12039112947262 小时前
从原生 JS 到 Vue3 Composition API:手把手教你用现代 Vue 写一个优雅的 Todos 任务清单
前端·vue.js·面试
Sherry0072 小时前
从零开始理解 JavaScript Promise:彻底搞懂异步编程
前端·javascript·promise
毛发浓密的女猴子2 小时前
一次弹窗异常引发的思考:iOS present / push 底层机制全解析
前端
Toomey2 小时前
一次 npm 更新强制2FA导致的发布失败的排查:403、2FA、Recovery Code、Granular Token 的混乱体验
前端
小满、2 小时前
Redis:数据结构与基础操作(String、List、Hash、Set、Sorted Set)
java·数据结构·redis·分布式锁
用户4445543654262 小时前
Android模块化管理
前端
alien爱吃蛋挞2 小时前
【JavaEE】Spring Boot日志
java·数据库·spring boot