打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
ASIO's coroutine

 ASIO's coroutine

本篇文章要说的时boost.asio示例代码example\http\server4中的coroutine的分析及其背后的一些历史线索。鉴于本人技术实力有限,另外只是对coroutine有兴趣,又是刚刚接触,如果有不正确的地方,还请多多指正。

关于coroutine

coroutine的介绍和实现请见这里。coroutine在处理异步的同步问题在某些情况下非常方便,会让代码的可读性提高很多,而且也会提高异步程序的开发速度。一些动态语言就只提供了coroutine作为并行机制,比如Lua。

coroutine的实现方式有很多,比如基于状态机,生成器什么的。根据特性又分为非对称和对称coroutine,根据存储方式的不同又分为stackless和stackful。

boost.asio.coroutine简介

在boost.asio中的example/http/server4中实现了一个基于状态机的coroutine的类。代码点这里

在代码中很神奇的两个关键字是reenteryield。在一般的coroutine的实现中,yield会交出执行权,返回到调用该coroutine的函数,当再次进入这个coroutine时,会继续从上次yield之后的语句开始执行。这就是coroutine所谓的多个入口点和多个返回出口。

boost.asio.coroutine实现原理

我把我写的example展开,发现了一些奥秘。展开后的代码见这里
仔细观察代码,实现yield的关键是那些case,大家在看的时候,把那些case -1, case 1的地方暂且放过,关注case 0还有case 999。case 0表示一段代码的起点,case 999表示yield的标记,标记这是某个yield。当执行某个yield时,会把当前yield的标记(这里是999)赋值给基类的成员变量_coro_value, 然后执行yield同行的代码。之后会退出reenter整个作用域,也就结束了整个函数。

当写很多的yield时,为了保证每个指令都有不同的标记,需要自动生成该标记。在asio.coroutine中是利用了__COUTER__宏,这个会由编译器产生不重复的标记。
既然我们知道了boost.asio.coroutine的精髓,那么我们自己实现一个。代码比较简单,状态变量使用的是static, 虽然没有考虑线程安全,但是足够使用,大家也知道,协程本身是一种线性的东西,所以也不用担心线程安全。代码见这里

boost.asio.coroutine背后的技术

看过上面的代码,大家肯定很好奇,asio实现的这种coroutine是基于了什么原理才能做到这样?
在大牛Simon Tatham(putty的作者)的一篇文章《Coroutine in C》. 中展示了如何把一个复杂的decompressor简化的过程。最终简化的代码是参考了Duff’s Device

关于Duff’s Device,请点这里.更详细的解释见这里这里

Duff’s Device的实现机制:

Based on an algorithm used widely by programmers coding in assembly for minimizing the number of tests and branches during a copy, Duff’s device appears out of place when implemented in C. The device is valid C by virtue of two attributes in C:

  • Relaxed specification of the switch statement in the language’s definition. At the time of the device’s invention this was the first edition of The C Programming Language which requires only that the controlled statement of the switch be a syntactically valid (compound) statement within which case labels can appear prefixing any sub-statement. In conjunction with the fact that, in the absence of a break statement, the flow of control will fall through from a statement controlled by one case label to that controlled by the next, this means that the code specifies a succession of count copies from sequential source addresses to the memory-mapped output port.

  • The ability to jump into the middle of a loop in C.

boost.asio.coroutine使用

boost.asio.coroutine的使用非常简单,在自己代码中包含coroutine.hpp和yield.hpp,创建一个类,派生自coroutine类。然后可以根据自己的业务要求写相应的coroutine了。比如开源项目avbot中的代码,可以看这里这里。当然,也可以把coroutine类作为自己的成员变量组合到自己的类中。

reenter

在boost.asio.coroutine中,reenter实际上一个宏。该宏的作用是定义一个coroutine block.它的输入参数是coroutine类,这也就是为什么使用继承和组合都可以的原因。类似的代码如下:

reenter(this) {    // coroutine body….}
或者
reenter (coroutine_) {    // coroutine body…..}

yield

在boost.asio.coroutine中,yield也是用宏实现的。虽然如此,但是它能够实现yield的大部分用法,比如yield taskpool.post(); 比如yield return 123; 比如yield;等等

asio的作者对自己实现coroutine做了详细的说明,请点这里。还有asio的作者写的这篇指南。下面是我写的一个example.其中task_pool_是一个线程池,可以投递任意函数执行,类似于asio.service。代码见这里

boost.asio.coroutine的局限

  • 协程之间的变量传递只能通过定义为类成员变量或全局的方式传递,因为boost.asio.coroutine的实现是基于状态机的,也就是switch…case…
  • C++不是原生支持coroutine,实现coroutine之间的变量传递会很复杂。相比起Golang和Erlang,C++实现的协程都是非常复杂的,而且接口不是太简单易用
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Unity3D教程:脚本初级知识(三)
游戏引擎Unity中的单线程与多线程
解决CPU严重消耗的问题
tornado.gen 模块解析
Lua 下实现抢占式多线程 | IT瘾
简要分析unity3d中剪不断理还乱的yield
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服