Swoole協程之旅-前篇

寫在最前

  Swoole協程經歷了幾個里程碑,咱們須要在前進的道路上不斷總結與回顧本身的發展歷程,正所謂溫故而知新,本系列文章將分爲協程之旅前、中、後三篇。javascript

  1. 前篇主要介紹協程的概念和Swoole幾個版本協程實現的主要方案技術;
  2. 中篇主要深刻Zend分析PHP部分的原理和實現;
  3. 後篇主要補充和分析協程4.x的實現。

軟文正式開始

協程是什麼?

  概念其實很早就出現了,摘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

Swoole1.x

   Swoole最初以高性能網絡通信引擎的姿態進入你們視線,Swoole1.x的編碼主要是異步回調的方式,雖然性能很是高效,但不少開發都會發現,隨着項目工程的複雜程度增長,以異步回調的方式寫業務代碼是和人類正常思惟相悖的,尤爲是回調嵌套多層的時候,不只開發維護成本指數級上升,並且出錯的概率也大幅增長。你們理想的編碼方式是:同步編碼獲得異步非阻塞的性能。因此Swoole很早的時候就開始了協程的探索。java

  最初的協程版本是基於PHP生成器GeneratorsYield的方式實現的,能夠參考PHP大神Nikita的早期博客的關於協程介紹。PHP和Swoole的事件驅動的結合能夠參考騰訊出團隊開源的TSF框架,咱們也在不少生產項目中使用了該框架,確實讓你們感覺到了,以同步編程的方式寫異步代碼的快感,然而,現實老是很殘酷,這種方式有幾個致命的缺點:python

  • 全部主動讓出的邏輯都須要yield關鍵字。這會給程序員帶來極大的機率犯錯,致使你們對協程的理解轉移到了對Generators語法的原理的理解。
  • 因爲語法沒法兼容老的項目,改造老的項目工程複雜度巨大,成本過高。

這樣使得不管新老項目,使用都沒法駕輕就熟。c++

Swoole2.x

   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

Swoole3.x

3.x是生命週期很短的一個版本,主要借鑑了fiber-ext項目,使用了PHP7的VM interrupts機制,該機制能夠在vm中設置標記位,在執行一些指令的時候(例如:跳轉和函數調用等)檢查標記位,若是命中就能夠執行相應的hook函數來切換vm的棧,進而實現協程。雖然咱們完整的實現了協程的功能,可是因爲並無相對2.x有很大的進步,緣由咱們後續的文章會作進一步分析,因此咱們放棄了這個版本,直接進入了4.x的版本迭代。golang

Swoole4.x

4.x協程是當前Swoole的協程版本,借鑑了前面版本的缺點和問題,引入了PHP+C雙棧管理維護,完美的支持PHP各類語法,詳細分析咱們放在系列文章最後。

End

協程之旅前篇結束,下一篇文章咱們將深刻Zend分析Swoole原生協程PHP部分的實現。

相關文章
相關標籤/搜索