Swoole协程经历了几个里程碑,咱们须要在前进的道路上不断总结与回顾本身的发展历程,正所谓温故而知新,本系列文章将分为协程之旅前、中、后三篇。javascript
概念其实很早就出现了,摘wiki一段:According to Donald Knuth, the term coroutine was coined by Melvin Conway in 1958, after he applied it to construction of an assembly program.The first published explanation of the coroutine appeared later, in 1963. 协程要比c语言的历史还要悠久,究其概念,协程是子程序的一种, 能够经过yield的方式转移程序控制权,协程之间不是调用者与被调用者的关系,而是彼此对称、平等的。协程彻底有用户态程序控制,因此也被成为用户态的线程。协程由用户以非抢占的方式调度,而不是操做系统。正由于如此,没有系统调度上下文切换的开销,协程有了轻量,高效,快速等特色。(大部分为非抢占式,可是,好比golang在1.4也加入了抢占式调度,其中一个协程发生死循环,不至于其余协程被饿死。须要在必要的时刻让出CPU,Swoole在V4.3.2增长了这个特性)。php
协程近几年如此火爆,很大一部分缘由归功与golang在中国的流行和快速发展,受到不少开发的喜好。目前支持协程的语言有不少,例如: golang、lua、python、c#、javascript等。你们也能够用很短的代码用c/c++撸出协程的模型。固然PHP也有本身的协程实现,也就是生成器,咱们这里不展开讨论。html
Swoole最初以高性能网络通信引擎的姿态进入你们视线,Swoole1.x的编码主要是异步回调的方式,虽然性能很是高效,但不少开发都会发现,随着项目工程的复杂程度增长,以异步回调的方式写业务代码是和人类正常思惟相悖的,尤为是回调嵌套多层的时候,不只开发维护成本指数级上升,并且出错的概率也大幅增长。你们理想的编码方式是:同步编码获得异步非阻塞的性能。因此Swoole很早的时候就开始了协程的探索。java
最初的协程版本是基于PHP生成器GeneratorsYield的方式实现的,能够参考PHP大神Nikita的早期博客的关于协程介绍。PHP和Swoole的事件驱动的结合能够参考腾讯出团队开源的TSF框架,咱们也在不少生产项目中使用了该框架,确实让你们感觉到了,以同步编程的方式写异步代码的快感,然而,现实老是很残酷,这种方式有几个致命的缺点:python
这样使得不管新老项目,使用都没法驾轻就熟。c++
2.x以后的协程都是基于内核原生的协程,无需yield关键字。2.0的版本是一个很是重要的里程碑,实现了php的栈管理,深刻zend内核在协程建立,切换以及结束的时候操做PHP栈。在Swoole的文档中也介绍了不少关于每一个版本实现的细节,咱们这篇文章只对每一个版本的协程驱动技术作简单介绍。原生协程都有对php栈的管理,后续咱们会单独拿一片文章来深刻分析PHP栈的管理和切换。git
2.x主要使用了setjmp/longjmp的方式实现协程,不少C项目主要采用这种方式实现try-catch-finally,你们也能够参考Zend内核的用法。setjmp的首次调用返回值是0,longjmp跳转时,setjmp的返回值是传给longjmp的value。 setjmp/longjmp因为只有控制流跳转的能力。虽然能够还原PC和栈指针,可是没法还原栈帧,所以会出现不少问题。好比longjmp的时候,setjmp的做用域已经退出,当时的栈帧已经销毁。这时就会出现未定义行为。假设有这样一个调用链:程序员
func0() -> func1() -> ... -> funcN()
只有在func{i}()中setjmp,在func{i+k}()中longjmp的状况下,程序的行为才是可预期的。github
3.x是生命周期很短的一个版本,主要借鉴了fiber-ext项目,使用了PHP7的VM interrupts机制,该机制能够在vm中设置标记位,在执行一些指令的时候(例如:跳转和函数调用等)检查标记位,若是命中就能够执行相应的hook函数来切换vm的栈,进而实现协程。虽然咱们完整的实现了协程的功能,可是因为并无相对2.x有很大的进步,缘由咱们后续的文章会作进一步分析,因此咱们放弃了这个版本,直接进入了4.x的版本迭代。golang
4.x协程是当前Swoole的协程版本,借鉴了前面版本的缺点和问题,引入了PHP
+C
双栈管理维护,完美的支持PHP各类语法,详细分析咱们放在系列文章最后。
协程之旅前篇结束,下一篇文章咱们将深刻Zend分析Swoole原生协程PHP部分的实现。