協程

一、協程的理解

協程,又稱微線程,纖程。英文名Coroutine,是一種用戶態的輕量級線程。python

注意:
  1. python的線程屬於內核級別的,即由操做系統控制調度(如單線程一旦遇到io就被迫交出cpu執行權限,切換其餘線程運行)
  2. 單線程內開啓協程,一旦遇到io,從應用程序級別(而非操做系統)控制切換
協程優勢:
  1. 協程的切換開銷更小,屬於程序級別的切換,操做系統徹底感知不到,於是更加輕量級
  2. 單線程內就能夠實現併發的效果,最大限度地利用cpu
協程缺點:
  1.協程的本質是單線程下,沒法利用多核,能夠是一個程序開啓多個進程,每一個進程內開啓多個線程,每一個線程內開啓協程
  2.協程指的是單個線程,於是一旦協程出現阻塞,將會阻塞整個線程編程

協程擁有本身的寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其餘地方(線程調度時候寄存器上下文及棧等保存在內存中),在切回來的時候,恢復先前保存的寄存器上下文和棧。所以:協程能保留上一次調用時的狀態(即全部局部狀態的一個特定組合),每次過程重入時,就至關於進入上一次調用的狀態,換種說法:進入上一次離開時所處邏輯流的位置。網絡

子程序,或者稱爲函數,在全部語言中都是層級調用,好比A調用B,B在執行過程當中又調用了C,C執行完畢返回,B執行完畢返回,最後是A執行完畢。多線程

因此子程序調用是經過棧實現的,一個線程就是執行一個子程序。子程序調用老是一個入口,一次返回,調用順序是明確的。而協程的調用和子程序不一樣。併發

協程看上去也是子程序,但執行過程當中,在子程序內部可中斷,而後轉而執行別的子程序,在適當的時候再返回來接着執行。less

注意,在一個子程序中中斷,去執行其餘子程序,不是函數調用,有點相似CPU的中斷。好比子程序A、B:dom

def A():
    print '1'
    print '2'
    print '3'
 
def B():
    print 'x'
    print 'y'
    print 'z'

假設由協程執行,在執行A的過程當中,能夠隨時中斷,去執行B,B也可能在執行過程當中中斷再去執行A,結果多是:異步

1
2
x
y
3
z

可是在A中是沒有調用B的,因此協程的調用比函數調用理解起來要難一些。異步編程

看起來A、B的執行有點像多線程,但協程的特色在因而一個線程執行。函數

二、yield的運用

從句法上看,協程與生成器相似,都是定義體中包含 yield 關鍵字的函數。但是,在協程中, yield 一般出如今表達式的右邊(例如, datum = yield),能夠產出值,也能夠不產出 —— 若是 yield 關鍵字後面沒有表達式,那麼生成器產出 None。

協程可能會從調用方接收數據,不過調用方把數據提供給協程使用的是 .send(datum) 方法,而不是next(…) 函數。

==yield 關鍵字甚至還能夠不接收或傳出數據。無論數據如何流動, yield 都是一種流程控制工具,使用它能夠實現協做式多任務:協程能夠把控制器讓步給中心調度程序,從而激活其餘的協程==。

import random
def create_generator_function(n):
    i = 0
    while i < n:
        b = yield i
        print("It's {0}".format(b))
        i = i + 1
obj = create_generator_function(5)
obj1 = obj.next()
x = 0
while x<5:
    try:
        obj1 = obj.send(random.random()) 
    except:
        pass
    x += 1

咱們先給協程一個標準定義,即符合什麼條件就能稱之爲協程:

  1. 必須在只有一個單線程裏實現併發
  2. 修改共享數據不需加鎖
  3. 用戶程序裏本身保存多個控制流的上下文棧
  4. 一個協程遇到IO操做自動切換到其它協程

基於上面這4點定義,咱們剛纔用yield實現的程並不能算是合格的線程,不知足最後一條。

 如何實現單線程下併發效果:遇到IO操做就切換,協程之因此能處理大併發,就是因爲擠掉了IO操做,使得CPU一直運行。

關鍵在於切換出來後,何時再切換回去??須要程序自動監測IO操做,IO操做結束就切換回去。

以上咱們是經過yeild實現的協程的功能,yield能實現協程,不過實現過程不易於理解,greenlet是在這方面作了改進。

三、Greenlet

Greenlet是python的一個C擴展,來源於Stackless python,旨在提供可自行調度的‘微線程’, 即協程。generator實現的協程在yield value時只能將value返回給調用者(caller)。 而在greenlet中,target.switch(value)能夠切換到指定的協程(target), 而後yield value。greenlet用switch來表示協程的切換,從一個協程切換到另外一個協程須要顯式指定。是一種手動切換

四、gevent

gevent是第三方庫,能夠輕鬆經過gevent實現併發同步或異步編程,在gevent中用到的主要模式是Greenlet, 它是以C擴展模塊形式接入Python的輕量級協程。 Greenlet所有運行在主程序操做系統進程的內部,但它們被協做式地調度。其基本思想是:

當一個greenlet遇到IO操做時,好比訪問網絡,就自動切換到其餘的greenlet,等到IO操做完成,再在適當的時候切換回來繼續執行。因爲IO操做很是耗時,常常使程序處於等待狀態,有了gevent爲咱們自動切換協程,就保證總有greenlet在運行,而不是等待IO。

因爲切換是在IO操做時自動完成,因此gevent須要修改Python自帶的一些標準庫,這一過程在啓動時經過monkey patch完成。

相關文章
相關標籤/搜索