基于协程io_uring 异步网络库系列 I: 异步和协程复习 | C++20 coroutine 教程 | io_uring 异步IO 网络框架 系列笔记

 系列概述:

cppcoro 源码级使用教程系列: 概述 | C++20 coroutine 教程 | io_uring 异步IO 网络框架 系列笔记_我说我谁呢 --CSDN博客


首先复习协程的部分先(对照 C++20 coroutine 探索 系列笔记, 建议对着第二篇的查阅备用来复习)。这可能是因为编写 awaiter 是如何的是很重要的(如果要支持用 co_await 写程序的话),所以还是要搞明白协程和 cppcoro 提供的协程包装(是C#早有的成熟系列)的执行过程。其实下面我会一直强化这个协程状态机的运行走向,就当作是复习巩固记忆了。

这个文章没有零基础意义,完全是基于https://blog.csdn.net/u010180372/category_11665488.htmlhttps://blog.csdn.net/u010180372/category_11665488.html专栏内的所有笔记本身是和他们自洽的(也许漏了一篇讲如何理解协程和函数式编程中的 call/cc 的笔记,博客中也上传了,当然实际这系列笔记不是一个能够快速上手的,而是一个系列的学习,主要目的是供我自己复习或者有对 C++ 协程与 Proactor 网络框架编写感兴趣的读者。


一个协程的返回值 task 是一个 R 类型兼 Awaiter 类型,他的 promise_type 的 initial_suspend 设置为了 suspend_always 所以只要他被运行,指 literally call 的时候,协程方法体还没有被运行,协程的 current continuation 就会直接被捕获到 task::promise_type 里面去了,此时我们设置的是把协程的 coroutine_handle 给存起来(即被存到堆上的某个位置上),然后 task(R)就被调用者获取到了。好了,现在另一个概念是 task 本身我们写成了 awaitable (注意 awaitable 只是一个概念,只要他能够支持 co_xxx keywords,即转成 awaiter,awaiter 才是状态机的本体)。我们可以在一个普通协程(或者说 syncwait,cppcoro 没有提供,实际只需要写一个 R 的 init、final suspend 都为 suspend_never 的即可 )中直接 co_await 一个 task,如果执行到了 co_await 一个 task 的时候,此时的状态机行为是会打包当前 caller current continuation 为 coroutine_handle 进入到 awaiter::await_suspend 里面去(作为参数),之后我们编写的代码就是存起 caller 的 cc,然后恢复 task 中的 continuation 执行,执行完毕后状态机进入收尾的 final_suspend, 我们在 final_suspend 的那个 awaitable 里面触发我们之前 caller 的 cc 去执行。(这段或对照第三篇的 task 实现源码来看)

对于只作为延时 task 存在的使用情况时,我们要做的是如果一个人 co_await 了 task,我们就把 task 的 continuation 给执行完后恢复 caller 的 continuation 从而达成延迟运行的效果。


我们来到真异步的范围了。仍然使用 task 包装协程(recall that task 是一个 R 兼 Awaiter),而我们的异步操作则是封装为 Awaiter(由于 Awaiter 本身也是 awaitable,所以后面会混淆一下说法,补充这里有误,实际如果要理解 awaitable 为具有 operator co_await  的类,二 awaiter 的可以 co_await 是编译器层面生成的代码)。这时要复习一下真异步的原理,对于我们第一个 asyncvoid 协程(即上面说的写一个 R 的 init、final suspend 都为 suspend_never 的),他相当于 C# 中的 async void 关键字(区分 async Task<void>),他,注意 asyncvoid 里面我们明白他实际是提交了异步任务(通过 co_await 运行真异步 awaiter 从而引发 continuation 被存了,因为必须要 OS 告诉我们,你的活干完了才能恢复 continuation)然后马上就返回的了。至于其中的 asyncvoid 的剩余部分的这个 continuation 实际你要自己执行回来在一个 I/O Completion 线程中。不过这里补充一些真实情况吧,真实情况是 task 他本身是把你 asyncvoid 的 continuation 给捕获保存了,只需要你最深层的 awaiter(真异步操作)把前一层的 task 的 continuation 给恢复就行了。你可以在脑海中看到一副递归的画面,是的这就是递归的画面。此时此刻,又能够理解 boost asio 中的那个诡异的协程的栈实现的 了,首先,考虑一幅这样的景象,一大堆 task 嵌套着直到最后的一层 awaitable(实际有很多次 awaitable,树状的)。boost asio 的 coroutine ts 支持还有一个 clear_cancellation_slot 的步骤,这我就不是很明白了。总之你可以像 cppcoro 这样通过状态机的过程完成整个链式结构的控制流执行,不过你跟踪理解起来会比较麻烦而已。asio 的做法是通过一种 awaitable_frame 的方法,构建了一个隐式的链式栈(而且这个 frame 还会通过 asio 的  thread 管理器绑定一个线程)。从而可以让 IO completion thread 直接循环执行完整个链条。而我们 task 的这种写法是通过让 IO completion thread 给执行最底层 awaitable 的 continuation 的 resume 之后,通过 R::promise_type 类型状态机本身的 final_suspend 来完成下一层次的 resume(类似一系列函数调用)。我不知道我说明白了没有或者是不是说错了。


请确定能够从一个没有对照着代码或者画图的思路上完全从想象明白上面的流程,这样就能无障碍进入到下一篇了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值