天天看點

協程(一)快速了解協程的定義和分類協程的定義協程如何工作控制傳遞機制stackfulnessfirst-class對象參考資料

    協程的概念很早以前就被提出,很多語言也實作了協程,例如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語言協程庫》

繼續閱讀