python greenlet背景介紹與實現機制

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

 併發處理的技術背景

並行化處理目前很受重視, 由於在不少時候,並行計算能大大的提升系統吞吐量,尤爲在如今多核多處理器的時代, 因此像lisp這種古老的語言又被人們從新拿了起來, 函數式編程也愈來愈流行。 介紹一個python的並行處理的一個庫: greenlet。 python 有一個很是有名的庫叫作 stackless ,用來作併發處理, 主要是弄了個叫作tasklet的微線程的東西, 而greenlet 跟stackless的最大區別是, 他很輕量級?不夠, 最大的區別是greenlet須要你本身來處理線程切換, 就是說,你須要本身指定如今執行哪一個greenlet再執行哪一個greenlet。程序員

greenlet的實現機制

之前使用python開發web程序,一直使用的是fastcgi模式.而後每一個進程中啓動多個線程來進行請求處理.這裏有一個問題就是須要保證每一個請求響應時間都要特別短,否則只要多請求幾回慢的就會讓服務器拒絕服務,由於沒有線程可以響應請求了.平時咱們的服務上線都會進行性能測試的,因此正常狀況沒有太大問題.可是不可能全部場景都測試到.一旦出現就會讓用戶等很久沒有響應.部分不可用致使所有不可用.後來轉換到了coroutine,python 下的greenlet.因此對它的實現機制作了一個簡單的瞭解.
每一個greenlet都只是heap中的一個python object(PyGreenlet).因此對於一個進程你建立百萬甚至千萬個greenlet都沒有問題.web

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
A PyGreenlet is a range of C stack addresses that must be
saved and restored in such a way that the full range of the
stack contains valid data when we switch to it.
 
Stack layout for a greenlet:
 
                |     ^^^       |
                |  older data   |
                |               |
   stack_stop . |_______________|
         .      |               |
         .      | greenlet data |
         .      |   in stack    |
         .    * |_______________| . .  _____________  stack_copy + stack_saved
         .      |               |     |             |
         .      |     data      |     |greenlet data|
         .      |   unrelated   |     |    saved    |
         .      |      to       |     |   in heap   |
  stack_start . |     this      | . . |_____________| stack_copy
                |   greenlet    |
                |               |
                |  newer data   |
                |     vvv       |

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)協程有編程語言提供,由程序員控制進行切換,因此沒有線程安全問題,能夠用來處理狀態機,併發請求等。可是沒法利用多核優點。
上面的四種方案能夠配合使用,我比較看好的是進程+協程的模式。併發

相關文章
相關標籤/搜索