漫谈C++20协程

开发
本篇文章我们只简单地介绍一下c++20协程,起到抛砖引玉效果,感兴趣的读者可以更深入阅读相关文献。

1、协程是什么  

协程可以理解为特数的函数,即可挂起与恢复的函数,而我们的普通函数只能一直执行到底,有没有感觉比普通的函数更厉害一些?协程一般都可以被划分为两大类,一类是有栈(tickful) 协程,例如goroutine,libco等;另一类是无栈(stackless)协程,例如C++、C#等。

这里说明一点,所谓的有栈、无栈并不是说这个协程运行的时候有没有栈,而是说协程之间是否存在调用栈,C++20协程是属于后者,协程之间调用就是通过在堆上分配协程帧空间实现的。

2、C++20协程 

C++ 20标准引入了协程(coroutine)从此C++进入了协程时代,不过C++20协程只是包含了编译器需要的底层功能,开发者使用相对复杂一些,进行封装后才可以进一步使用。

2.1 C++20协程整体流程

下面我们看一下C++20协程的整体执行流程:

图片

图1

针对上图1中的每个模块释义如下:

0、开始执行协程;

1、首先在堆上分配协程帧,大小包括实参大小,局部变量大小(包括寄存器),promise_type对象大小,协程状态等;

2、把协程的实参拷贝到协程帧中;

3、构造promise_type对象,promise_type构造函数如果与协程的构造函数完全一致,那么调用这个构造函数,否则调用默认构造函数;

4、通过promise_type对象来构造协程的返回类型Corouting_obj对象;

5、执行表达式co_awaitpromise_type.inintial_suspend(),可以挂起,此处为定制点,可以自定义协程行为;

6、如果协程在initial_suspend处挂起了,则需要等待回复进行执行;

7、协程函数体代码执行;

8、通过co_return语句返回时,将转换成对promise_type类型的return_void或return_value接口进行调用,二者选其中之一。具体看co_return后面跟着的表达式,如果没有表达式或者是void,转换为return_void,否则转换为return_value。co_return语句表示这个协程即将结束其生命期;

9、协程体内发生了异常,由unhandled_exception接口对异常进行处理;

10、final_suspend允许协程结束前挂起,当协程体内抛出异常或者通过co_return返回时都将到达这个挂起点,可以自定义行为,但不能抛出异常,所以实现时需要被修饰为noexcept;

11、堆内存申请失败,处理异常函数

promise_type::get_return_object_on_allocation_failure();

2.2 C++20协程特点

2.1节介绍了C++协程的整个执行过程,那么c++20为开发者实现协程又提供了哪些新的关键词、 以及什么样的函数看作是协程而不是普通函数呢?下面我们简单介绍一下C++20协程一些特点:

1、如果函数中包含了co_return, co_await, co_yield中的任意一个,这个函数就会被当作协程来处理;

2、协程被挂起后那么其局部变量或状态等需要被保存下来,通过操作协程句柄(corouting_handle)来管理写协程。协程句柄是一个对象,包含了分配在堆上协程帧指针和一些操作协程成员函数;

3、协程中promise_type类型能够让我们定制的有initial_suspend、final_suspend、yield_value、return_value或return_void、unhandled_exception等行为,这些行为在协程被调用时生效;

4、co_await表达式涉及了awaitable对象、awaiter对象相关概念,可以通过定义await_transform函数得到awaitable对象,可以通过重载operator co_await操作符得到awaiter对象;

2.3 C++20协程使用示例代码

下面我们通过协程实现一个简单的生成器(Generator),具体代码示例如下,代码亲测可跑,只要支持C++20特性的C++编译器即可:

#include <iostream>
#include <coroutine>
template<typename T>
struct Generator{
public:
    struct ValueAwaiter{
        constexpr bool await_ready(){
            return false;
        }
        template<typename promise_type>
        void await_suspend(std::coroutine_handle<promise_type> h){
            h.promise().value_ = value_;
        }
        void await_resume(){}
        T value_;
    };
    struct promise_type{
        std::suspend_always initial_suspend() noexcept{
            return {};
        }
        std::suspend_always final_suspend() noexcept{
            return {};
        }
        Generator get_return_object(){
            return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
        }
        void unhandled_exception(){
            throw std::current_exception();
        }
        void return_void(){
        }
        auto await_transform(T v){
            return ValueAwaiter{v};
        }
        std::suspend_always yield_value(T v){
            value_ =v;
            return {};
        }
    public:
        T value_;
    };
    T next(){
        handle_.resume();
        return handle_.promise().value_;
    }
public:
    std::coroutine_handle<promise_type> handle_;
};
Generator<int> get_number(){
    int i{0};
    while (true){
        co_yield i++;
    }
}
int main(){
    auto g = get_number();
    for(int j = 0; j < 10; ++j){
        std::cout << g.next() << std::endl;
    }
}

上面代码中Gernerator就是一个协程object,函数get_number满足C++20协程特性使用了co_yield,因此是一个协程,而不是普通函数。执行结果如下:

图片

3、总结 

C++20只是提供了简单的协程特性,开发人员如果要用到生产环境,就需要自己开发相关库,相对难度较大一些,在未来我们可以期待有更多更好的协程库 进入后续的C++标准(C++23, C++26), 这样开发人员更好的使用协程了。

责任编辑:庞桂玉 来源: 移动Labs
相关推荐

2022-09-06 20:30:48

协程Context主线程

2022-09-12 06:35:00

C++协程协程状态

2022-09-10 18:51:09

C++协程主线程

2021-09-16 09:59:13

PythonJavaScript代码

2024-01-19 21:07:22

C++20Concepts函数

2020-12-21 21:05:29

C++C++20标准草案

2023-11-17 11:36:59

协程纤程操作系统

2021-10-12 07:15:03

C++20特性

2024-02-05 22:13:50

C++C++20开发

2023-10-24 19:37:34

协程Java

2014-02-11 09:28:57

2021-12-09 06:41:56

Python协程多并发

2016-10-28 17:39:47

phpgolangcoroutine

2024-02-05 09:06:25

Python协程Asyncio库

2020-11-29 17:03:08

进程线程协程

2023-12-24 12:56:36

协程

2017-05-02 11:38:00

PHP协程实现过程

2023-08-08 07:18:17

协程管道函数

2021-04-25 09:36:20

Go协程线程

2021-08-04 16:19:55

AndroidKotin协程Coroutines
点赞
收藏

51CTO技术栈公众号