协程(一)快速理解协程的定义和分类

来源:互联网 发布:怎样领取淘宝优惠券 编辑:程序博客网 时间:2024/06/05 10:38

    协程的概念很早以前就被提出,很多语言也实现了协程,例如Erlang,Lua。不过我首次接触协程是在学习golang的过程中,当真正使用协程的时候就被它的优雅和高效所折服,这也埋下了自己对协程好奇的种子。随着接触许多C++协程库,例如云风的coroutine、腾讯的libco、魅族的libgo等,很多当时模糊的概念也逐渐清晰。

   云风的coroutine代码简介,实现了非对称的stackful协程,非常适合用来入门。笔者fork了该库,并根据自己对协程工作原理的理解为该库添加了注释,详细可参考我的github.

协程的定义

    在计算机科学中,例程(过程、函数、方法、子程序)被定义为操作的序列。例程的执行形成了父子关系,且孩子例程总是在父例程前结束。举例说明,main函数中调用函数func1,func1中调用函数func2,此时就形成了父子关系(main为func1的父例程,func1是func2的父例程),func2执行结束进行函数栈回退,然后func1继续执行直到结束后进行函数栈回退,最后main继续执行直到结束后进行函数栈回退并退出程序。

   协程是例程(过程、函数、方法、子程序)的范化概念。协程和例程的主要区别是,协程通过保持执行状态可以能够明确的挂起和恢复,协程通过维护上下文提供了增强的控制流。

   2004年Lua的作者Ana Lucia de Moura和Roberto Ierusalimschy发表的论文Revisiting Coroutines中,对协程进行了分类,论文中依照三个问题区分协程:

  • 控制传递(Control-transfer)机制
  • 协程是否为栈式(Stackful)构造
  • 协程是否作为语言的第一类(First-class)对象提供

协程如何工作

   如果协程像例程一样,那么函数栈将会随着每次调用而增长、并且从不退栈。跳转到中间某个协程则是不可能的,因为函数栈的栈顶才能进行退栈操作。

   解决方法则是让每个协程拥有自己的栈和控制块(control-block)。在协程挂起前,该协程的non-volatile寄存器值(包括stack、instruction/program pointer)被保存在该协程对应的control-block中。新激活协程在恢复前,它在control-block中存储的寄存器值被恢复到CPU的寄存器中。

   上下文切换无需系统权限,为协作式多任务提供了方便。协程提供了并行性,例如当程序在同时做多件事情时,相比于只用一个单独的控制流,协程可以更加简单而优雅的做到这一点。

控制传递机制

   根据控制传递机制的不同区分出了对称协程和非对称协程。

   非对称协程知道它的调用者,其在挂起时转让控制权给它的调用者,然后调用者根据算法调用其他非对称协程进行工作。相比之下,所有的对称协程都是等价的,控制权直接在对称协程之间进行传递,即对称协程在挂起时主动指明另外一个对称协程来接收控制权。

stackfulness

   对比stackless协程,stackful协程可以在内部的嵌套函数中被挂起,在挂起点恢复执行。而stackless协程仅能在顶层部分被挂起。

first-class对象

   协程被作为first-class对象提供,那么其可以作为参数被传递,由函数创建并返回,并存储在一个数据结构中供后续操作,也可以被开发者自由的维护。从上面的描述可以看到,first-class对象主要为协程提供了良好的表达力,方便开发者对协程进行操作。


如果没有stackfulness和irst-class对象的语义,协程的能力将被限制。

参考资料

《Coroutine - Wiki》
《理解Lua的Coroutine》
《协程(一)协程的定义与分类》
《一个”蝇量级”C语言协程库》

0 0
原创粉丝点击