當前位置:
首頁 > 知識 > python greenlet 背景介紹與實現機制

python greenlet 背景介紹與實現機制

點擊上方「

Python開發

」,選擇「置頂公眾號」


關鍵時刻,第一時間送達!






最近開始研究Python的並行開發技術,包括多線程,多進程,協程等。逐步整理了網上的一些資料,今天整理一下greenlet相關的資料。



並發處理的技術背景




並行化處理目前很受重視, 因為在很多時候,並行計算能大大的提高系統吞吐量,尤其在現在多核多處理器的時代, 所以像lisp這種古老的語言又被人們重新拿了起來, 函數式編程也越來越流行。 介紹一個python的並行處理的一個庫: greenlet。 python 有一個非常有名的庫叫做 stackless ,用來做並發處理, 主要是弄了個叫做tasklet的微線程的東西, 而greenlet 跟stackless的最大區別是, 他很輕量級?不夠, 最大的區別是greenlet需要你自己來處理線程切換, 就是說,你需要自己指定現在執行哪個greenlet再執行哪個greenlet。




greenlet的實現機制




以前使用python開發web程序,一直使用的是fastcgi模式.然後每個進程中啟動多個線程來進行請求處理.這裡有一個問題就是需要保證每個請求響應時間都要特別短,不然只要多請求幾次慢的就會讓伺服器拒絕服務,因為沒有線程能夠響應請求了.平時我們的服務上線都會進行性能測試的,所以正常情況沒有太大問題.但是不可能所有場景都測試到.一旦出現就會讓用戶等好久沒有響應.部分不可用導致全部不可用.後來轉換到了coroutine,python 下的greenlet.所以對它的實現機製做了一個簡單的了解.



每個greenlet都只是heap中的一個python object(PyGreenlet).所以對於一個進程你創建百萬甚至千萬個greenlet都沒有問題.





typedef

struct

_greenlet

{


PyObject_HEAD


char

*

stack_start

;


char

*

stack_stop

;


char

*

stack_copy

;


intptr_t

stack_saved

;


struct

_greenlet

*

stack_prev

;


struct

_greenlet

*

parent

;


PyObject

*

run_info

;

struct

_frame

*

top_frame

;


int

recursion_depth

;


PyObject

*

weakreflist

;


PyObject

*

exc_type

;


PyObject

*

exc_value

;


PyObject

*

exc_traceback

;


PyObject

*

dict

;


}

PyGreenlet

;




每一個greenlet其實就是一個函數,以及保存這個函數執行時的上下文.對於函數來說上下文也就是其stack..同一個進程的所有的greenlets共用一個共同的操作系統分配的用戶棧.所以同一時刻只能有棧數據不衝突的greenlet使用這個全局的棧.greenlet是通過stack_stop,stack_start來保存其stack的棧底和棧頂的,如果出現將要執行的greenlet的stack_stop和目前棧中的greenlet重疊的情況,就要把這些重疊的greenlet的棧中數據臨時保存到heap中.保存的位置通過stack_copy和stack_saved來記錄,以便恢復的時候從heap中拷貝回棧中stack_stop和stack_start的位置.不然就會出現其棧數據會被破壞的情況.所以應用程序創建的這些greenlet就是通過不斷的拷貝數據到heap中或者從heap中拷貝到棧中來實現並發的.對於io型的應用程序使用coroutine真的非常舒服.




下面是greenlet的一個簡單的棧空間模型(from greenlet.c)









下面是一段簡單的greenlet代碼.





from

greenlet

import

greenlet


 


def

test1

()

:


    

print

12


    

gr2

.

switch

()


    

print

34


 


def

test2

()

:


    

print

56


    

gr1

.

switch

()


    

print

78


 


gr1

=

greenlet

(

test1

)


gr2

=

greenlet

(

test2

)


gr1

.

switch

()




目前所討論的協程,一般是編程語言提供支持的。目前我所知提供協程支持的語言包括python,lua,go,erlang, scala和rust。協程不同於線程的地方在於協程不是操作系統進行切換,而是由程序員編碼進行切換的,也就是說切換是由程序員控制的,這樣就沒有了線程所謂的安全問題。




所有的協程都共享整個進程的上下文,這樣協程間的交換也非常方便。




相對於第二種方案(I/O多路復用),使得使用協程寫的程序將更加的直觀,而不是將一個完整的流程拆分成多個管理的事件處理。




協程的缺點可能是無法利用多核優勢,不過,這個可以通過協程+進程的方式來解決。




協程可以用來處理並發來提高性能,也可以用來實現狀態機來簡化編程。我用的更多的是第二個。去年年底接觸python,了解到了python的協程概念,後來通過pycon china2011接觸到處理yield,greenlet也是一個協程方案,而且在我看來是更可用的一個方案,特別是用來處理狀態機。




目前這一塊已經基本完成,後面抽時間總結一下。




總結一下:






  • 1)多進程能夠利用多核優勢,但是進程間通信比較麻煩,另外,進程數目的增加會使性能下降,進程切換的成本較高。程序流程複雜度相對I/O多路復用要低。



  • 2)I/O多路復用是在一個進程內部處理多個邏輯流程,不用進行進程切換,性能較高,另外流程間共享信息簡單。但是無法利用多核優勢,另外,程序流程被事件處理切割成一個個小塊,程序比較複雜,難於理解。



  • 3)線程運行在一個進程內部,由操作系統調度,切換成本較低,另外,他們共享進程的虛擬地址空間,線程間共享信息簡單。但是線程安全問題導致線程學習曲線陡峭,而且易出錯。



  • 4)協程有編程語言提供,由程序員控制進行切換,所以沒有線程安全問題,可以用來處理狀態機,並發請求等。但是無法利用多核優勢。




上面的四種方案可以配合使用,我比較看好的是進程+協程的模式。






  • 來源:offbye 的博客




  • blog.csdn.net/offbye/article/details/39368781



  • Python開發整理髮布,轉載請聯繫作者獲得授權


【點擊成為Java大神】

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發 的精彩文章:

Python爬蟲之天氣預報
super 沒那麼簡單

TAG:Python開發 |