跳至正文

用python来理解C++20协程的设计

C++20拥有一个全新的特性:协程。

我来从python的角度来解释C++这个特性设计与其他语言的不同,目的以及意义。

协程是一种可以挂起和恢复执行的函数。C++20协程跟python的生成器是很相似的,如果函数中出现了co_yield, co_return, co_await,那么这个函数就是协程函数。而协程的本质就是将一个函数拆分成多个不能控制执行顺序,但是可以控制执行时机的语言结构。

对普通函数而言,执行一个函数是开始运行你的逻辑;但是对于协程来说,执行协程函数会先创建一个协程对象,但是是否开始执行还是挂起是视情况而定的。

例如这个python代码:

def hello():
    print("Start run")
    ret = yield2
    print("After suspend", ret)
    yield3
    print("After Suspend")

def main():
    generator=hello()
    print(generator)
    ret1 = next(generator)
    print("ret1: ", ret1)
    ret2 = generator.send(23)
    print("ret2: ", ret2)
    generator.send(23)

在这里hello就是一个协程函数,执行之后会获得一个generator,对generator执行send或者next才会开始真正的执行协程逻辑,从头或者从上次执行的地方一直直行到下一个yield,然后再挂起返回给外面。

python的生成器跟C++协程很像,都是无栈协程,但是C++的更复杂,因为C++是需要自定义协程对象。
例如这个简单的C++逻辑:

TaskCoroutine task_func() {
    std::cout << "task first run" << std::endl;
    co_yield 6;
    std::cout << "---before await task2---" << std::endl;
    co_await task2();
    std::cout << "task resume" << std::endl;
    co_return 3;
}

int main() {
    std::cout << "Before task_func" << std::endl; 
    TaskCoroutine load_task = task_func();
    std::cout << "After task_func" << std::endl;
    load_task.resume();
    std::cout << "After resume" << std::endl;
    return 0;
}

这里因为有co_yield,所以task_func是一个协程函数,但是跟python不同,C++需要定义携程的返回类型,是一个自定义类型,代表的这个携程的Task或者Coroutine。

这个类型类型必须包含promise_type名字的PromiseType类,例如:

struct TaskCoroutine
{
    using promise_type = PromiseType;
}

没有这个那编译就会异常,感觉这个是类似于C++20的概念约束做的。

promise_type又是另外一个约束的类,必须拥有几个必须定义的函数,以及几个可选定义的函数:

struct PromiseType {
    PromiseType()
    {
        std::cout << "PromiseType" << std::endl;
    }
    TaskCoroutine get_return_object() {
        std::cout << "get_return_object" << std::endl;
        return TaskCoroutine{std::coroutine_handle<PromiseType>::from_promise(*this)};
    }
    std::suspend_never initial_suspend() noexcept {
        std::cout << "initial_suspend" << std::endl;
        return {};
    }
    Awaiter<true> final_suspend() noexcept {
        std::cout << "final_suspend" << std::endl;
        return {};
    }
    void unhandled_exception() {
        std::cout << "unhandled_exception" << std::endl;
    }
    std::suspend_always yield_value(int x) noexcept {
        std::cout << "yield_value" << std::endl;
        return {};
    }
    void return_value(int x) noexcept {
        std::cout << "return_value" << std::endl;
    }
};

剩下的内容实在是不想写了,看我的视频吧:
https://www.bilibili.com/video/BV1H66aYTE84

标签:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注