C++ 20 协程 之 Hello World(一)
C++ 20 协程
C++协程可以提高资源利用率,利用状态机解决回调地狱的问题。与Kotlin协程具有相似之处,但是使用难度较为繁琐。
Hello World 非挂起
C++协程最为简单的Hello World案例如下:
#include <coroutine>
#include <iostream>
struct HelloCoroutine{
    HelloCoroutine() {
        std::cout << "HelloCoroutine constructed\n";
    }
    ~HelloCoroutine() {
        std::cout << "HelloCoroutine destructed\n";
    }
    struct promise_type{
        promise_type(){
            std::cout << "promise_type constructed\n";
        }
        ~promise_type(){
            std::cout << "promise_type destructed\n";
        }
        HelloCoroutine get_return_object(){
            std::cout << "get_return_object\n";
            return {};
        }
        std::suspend_never initial_suspend(){
            std::cout << "initial_suspend\n";
            return {};
        }
        std::suspend_never final_suspend() noexcept {
            std::cout << "final_suspend\n";
            return {};
        }
        void return_void(){
            std::cout << "return_void\n";
        }
        void unhandled_exception(){
            std::cerr << "Coroutine encountered an exception.\n";
        }
    };
};
HelloCoroutine helloCoroutine(){
    std::cout << "Hello, Coroutine!" << std::endl;
    // throw std::runtime_error("Coroutine cannot be suspended initially."); // 1
    co_return;
}
void learn01(){
    helloCoroutine();
    std::cout << "This is learn01 function." << std::endl;
}
/*
输出如下:
promise_type constructed
get_return_object
HelloCoroutine constructed
initial_suspend
Hello, Coroutine!
return_void
final_suspend
promise_type destructed
HelloCoroutine destructed
This is learn01 function.
============
打开1处的注释的执行结果如下:
promise_type constructed
get_return_object
HelloCoroutine constructed
initial_suspend
Hello, Coroutine!
Coroutine encountered an exception.
final_suspend
promise_type destructed
HelloCoroutine destructed
This is learn01 function.
*/
分析一下,根据返回结果可以推测出helloCoroutine被转换为以下函数:
HelloCoroutine helloCoroutine1(){
    coroutine_frame *frame = new coroutine_frame();
    HelloCoroutine::promise_type &promise = frame->promise;
    HelloCoroutine return_object = promise.get_return_object(); // init HelloCoroutine
    co_await promise.initial_suspend();
    try{
        std::cout << "Hello, Coroutine!" << std::endl;
        promise.return_void(); // co_return;
    }catch(...){
        promise.unhandled_exception();
    }
    co_await promise.final_suspend();
}
Hello World 挂起
struct HelloCoroutine{
public:
    struct promise_type{
        promise_type(){
            std::cout << "promise_type constructed\n";
        }
        ~promise_type(){
            std::cout << "promise_type destructed\n";
        }
        HelloCoroutine get_return_object(){
            std::cout << "get_return_object\n";
            return HelloCoroutine(std::coroutine_handle<promise_type>::from_promise(*this));
        }
        std::suspend_always initial_suspend(){
            std::cout << "initial_suspend\n";
            return {};
        }
        std::suspend_never final_suspend() noexcept { // 1
            std::cout << "final_suspend\n";
            return {};
        }
        void return_void(){
            std::cout << "return_void\n";
        }
        void unhandled_exception(){
            std::cerr << "Coroutine encountered an exception.\n";
        }
    };
    void resume(){
        if(!handle.done()){
            handle.resume(); // 2
        }
    }
    HelloCoroutine(std::coroutine_handle<promise_type> coroutineHandle) : handle(coroutineHandle) {
        std::cout << "HelloCoroutine constructed\n";
    }
    ~HelloCoroutine() {
        std::cout << "HelloCoroutine destructed\n";
	// handle.destroy(); 3
    }
private:
    std::coroutine_handle<promise_type> handle; // 指向协程帧
};
HelloCoroutine helloCoroutine(){
    std::cout << "Hello, Coroutine!" << std::endl;
    co_return;
}
void learn01(){
    HelloCoroutine hello = helloCoroutine();
    hello.resume();
    hello.resume();
    std::cout << "This is learn01 function." << std::endl;
}
/*
执行结果
promise_type constructed
get_return_object
HelloCoroutine constructed
initial_suspend
Hello, Coroutine!
return_void
final_suspend
promise_type destructed
This is learn01 function.
HelloCoroutine destructed
*/
代码分析
使用co_return的函数在调用时会在堆上创建coroutine_frame对象。该对象会保持以下数据
- 局部变量
- promise_type
- 状态机信息
源码查看:
// 17.12.5 Trivial awaitables
/// [coroutine.trivial.awaitables]
struct suspend_always
{
	constexpr bool await_ready() const noexcept { return false; }
	constexpr void await_suspend(coroutine_handle<>) const noexcept {}
	constexpr void await_resume() const noexcept {}
};
struct suspend_never
{
	constexpr bool await_ready() const noexcept { return true; }
	constexpr void await_suspend(coroutine_handle<>) const noexcept {}
	constexpr void await_resume() const noexcept {}
};
所以首先执行promise_type的构造函数。之后调用get_return_object创建HelloCoroutine对象。之后调用initial_suspend返回std::suspend_always代表永久阻塞,当耗时任务执行完毕,必须调用handle的resume方法恢复协程,在复杂应用中,为防止被多次resume程序奔溃,使用handle的done方法判断是否已经resume过,没有被resume过,则resume。紧接着调用
helloCoroutine的方法体,有异常的话执行unhandled_exception处理异常,没有异常正常执行,之后走到co_return;语句,则执行return_void方法处理返回逻辑。接着执行final_suspend方法结束协程,当返回std::suspend_never代表不挂起协程,直接结束协程,则立马销毁协程帧、HelloCoroutine对象。当返回std::suspend_always代表挂起协程,必须在HelloCoroutine对象的析构函数中调用handle的destroy方法销毁协程帧率对象,否则内存泄漏。
Hello World 返回内容
struct HelloCoroutine{
public:
    struct promise_type{
    private:
        int value; // 用于存储协程的返回值, 默认0
    public:
        promise_type(){
            std::cout << "promise_type constructed\n";
        }
        ~promise_type(){
            std::cout << "promise_type destructed\n";
        }
        HelloCoroutine get_return_object(){
            std::cout << "get_return_object\n";
            return HelloCoroutine(std::coroutine_handle<promise_type>::from_promise(*this)); // 解引用
        }
        std::suspend_always initial_suspend(){
            std::cout << "initial_suspend\n";
            return {};
        }
        std::suspend_always final_suspend() noexcept {
            std::cout << "final_suspend\n";
            return {};
        }
        void return_value(const int value){
            this->value = value;
            std::cout << "return_void with value: " << value << "\n";
        }
        void unhandled_exception(){
            std::cerr << "Coroutine encountered an exception.\n";
        }
        int getValue(){
            return value;
        }
    };
    int getValue() const { return handle.promise().getValue(); }
    void resume(){
        if(!handle.done()){
            handle.resume();
        }
    }
    HelloCoroutine(std::coroutine_handle<promise_type> coroutineHandle) : handle(coroutineHandle) {
        std::cout << "HelloCoroutine constructed\n";
    }
    ~HelloCoroutine() {
        std::cout << "HelloCoroutine destructed\n";
        handle.destroy();
    }
private:
    std::coroutine_handle<promise_type> handle; // 指向协程帧
};
HelloCoroutine helloCoroutine(){
    std::cout << "Hello, Coroutine!" << std::endl;
    co_return 520;
}
void learn01(){
    HelloCoroutine hello = helloCoroutine();
    hello.resume();
    std::cout << "Coroutine returned value: " << hello.getValue() << "\n";
}
/*
执行结果如下:
promise_type constructed
get_return_object
HelloCoroutine constructed
initial_suspend
Hello, Coroutine!
return_void with value: 520
final_suspend
Coroutine returned value: 520
HelloCoroutine destructed
promise_type destructed
*/
Hello World 生成器
struct HelloCoroutine {
public:
    struct promise_type {
    private:
        int value; // 用于存储协程的返回值, 默认0
    public:
        promise_type(){
            std::cout << "promise_type constructed\n";
        }
        ~promise_type(){
            std::cout << "promise_type destructed\n";
        }
        HelloCoroutine get_return_object(){
            std::cout << "get_return_object\n";
            return HelloCoroutine(std::coroutine_handle<promise_type>::from_promise(*this)); // 解引用
        }
        std::suspend_always initial_suspend(){
            std::cout << "initial_suspend\n";
            return {};
        }
        std::suspend_always final_suspend() noexcept {
            std::cout << "final_suspend\n";
            return {};
        }
        std::suspend_always yield_value(int value){
            this->value = value;
            std::cout << "yield_value with value: " << value << "\n";
            return {};
        }
        void unhandled_exception(){
            std::cerr << "Coroutine encountered an exception.\n";
        }
        int getValue(){
            return value;
        }
    };
    int getValue() const { return handle.promise().getValue(); }
    HelloCoroutine(std::coroutine_handle<promise_type> coroutineHandle) : handle(coroutineHandle) {
        std::cout << "HelloCoroutine constructed\n";
    }
    ~HelloCoroutine() {
        std::cout << "HelloCoroutine destructed\n";
        handle.destroy();
    }
    bool next() const {
        if(!handle.done()){
            handle.resume();
        }
        return !handle.done();
    }
private:
    std::coroutine_handle<promise_type> handle; // 指向协程帧
};
HelloCoroutine helloCoroutine(){
    std::cout << "Hello, Coroutine!" << std::endl;
    for(int i = 0; i < 5; ++i){
        co_yield i; // yield_value
    }
}
void learn01(){
    HelloCoroutine hello = helloCoroutine();
    while(hello.next()){
        std::cout << "Coroutine yielded value: " << hello.getValue() << "\n";
    }
}
TODO co_await, thread_pool, thread switch

 C++ 20 协程 之 Hello World
        C++ 20 协程 之 Hello World
     
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号