Coroutine
不过是一种特殊的subroutines
(Subroutines即过程调用)。以协程与函数类比比较,当调用一个函数时,程序从函数的起始地址开始执行,到函数执行结束返回,这就是整个函数的生命周期。函数在它的生命周期中,只可能返回一次。而协程在执行的过程中可以调用其他的协程,保护上下文切换到其他协程,之后协程调用返回恢复到调用的地址继续执行。这个过程类似于多线程的线程的切换。在这个过程中,协程与协程之间是对称的关系
(symmetric)。实际上,协程不一定都是这种对称的关系,还存在一种非对称的协程模式
(asymmetric coroutines)。非对称协程其实也比较常见,libco
其实就是一种非对称协程。
非对称协程
(asymmetric coroutines)是跟一个特定的调用者绑定的,协程让出CPU时,只能让回给调用者。何为非对称呢?在于协程的调用(call/resume
)和返回(return/yield
)的地位不对等
。程序控制流转移到被调用者协程,而被调用者只能返回最初调用它的协程。
对称协程
(symmetric coroutines)在于协程调用和返回的地位是对等的
。启动之后就跟启动之前的协程没有任何关系了。协程的切换操作,一般而言只有一个操作,yield
,用于将程序控制流转移给其他的协程。对称协程机制一般需要一个调度器的支持,按一定调度算法去选择yield的目标协程。
Go 语言提供的协程,其实就是典型的对称程。不但对称,goroutines 还可以在多个线程上迁移。这种协程跟操作系统中的线程非常相似,甚至可以叫做“用户级线程”了。而 libco 提供的协程,虽然编程接口跟pthread 有点类似,“类 pthread 的接口”,“如线程库一样轻松”,本质上却是一种非对称协程。这一点不要被表象蒙蔽了。事实上,libco 内部还为保存协程的调用链留了一个 stack 结构,而这个 stack 大小只有固定的 128。使用 libco,如果不断地在一个协程运行过程中去启动另一个协程,随着嵌套深度增加可能会造成这个栈空间溢出。
关于协程的图示:
协程的理解:
协程的本质与线程一样依然是执行控制流的实体,较之于线程:为了解决线程系统调用的花销,进一步细分出协程。协程调度是由用户态的协程库实现的,操作系统无法直接参与协程的调度。协程具有更轻量级的执行体也就是数据实体,协程的三要素-执行的函数,栈内存,协程控制块,只占较少的资源空间。创建、销毁、上下文切换等效率很高。但是协程也有其一定的缺陷,cpu的执行权限在线程的协程中,如果协程阻塞等那么整个线程都将被阻塞。体现了协程执行控制流的一致性。