Python之協程

什麼是協程

  協程,又稱微線程,纖程。英文名Coroutine。協程是一種用戶態的輕量級線程。協程擁有本身的寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其餘地方,在切回來的時候,恢復先前保存的寄存器上下文和棧。所以:協程能保留上一次調用時的狀態,每次過程重入時,就至關於進入上一次調用的狀態,換種說法:進入上一次離開時所處邏輯流的位置。python

協程優勢

  • 無需線程上下文切換的開銷
  • 無需原子操做鎖定及同步的開銷
  • 方便切換控制流,簡化編程模型
  • 高併發+高擴展性+低成本:一個CPU支持上萬的協程都不是問題。因此很適合用於高併發處理。

PS: "原子操做(atomic operation)是不須要synchronized",所謂原子操做是指不會被線程調度機制打斷的操做;這種操做一旦開始,就一直運行到結束,中間不會有任何 context switch (切換到另外一個線程)。原子操做能夠是一個步驟,也能夠是多個操做步驟,可是其順序是不能夠被打亂,或者切割掉只執行部分。視做總體是原子性的核心。編程

協程缺點

  • 沒法利用多核資源:協程的本質是個單線程,它不能同時將 單個CPU 的多個核用上,協程須要和進程配合才能運行在多CPU上.固然咱們平常所編寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用。
  • 進行阻塞(Blocking)操做(如IO時)會阻塞掉整個程序

協程引入

  以前咱們有用過yield關鍵字將用過函數變成一個生成器,實現了在單線程狀況下的併發效果,以下例所示:併發

import time
import queue
def consumer(name):
    print("--->starting eating baozi...")
    while True:
        new_baozi = yield
        print("[%s] is eating baozi %s" % (name,new_baozi))
        time.sleep(1)
 
def producer():
 
    r = con.__next__()
    r = con2.__next__()
    n = 0
    while n < 5:
        n +=1
        con.send(n)
        con2.send(n)
        print("\033[32;1m[producer]\033[0m is making baozi %s" %n )
 
 
if __name__ == '__main__':
    con = consumer("c1")
    con2 = consumer("c2")
    p = producer()
View Code

  那麼這算不算協程呢?咱們先要知道到底知足什麼條件纔算是一個協程。異步

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

  若是知足了以上的條件咱們就能夠認爲是一個協程。因此yield並徹底算是一個協程,由於它不能實現遇到IO操做就自動切換到其餘協程的功能。ide

Greenlet

  greenlet是一個用C實現的協程模塊,相比與python自帶的yield,它可使你在任意函數之間隨意切換,而不需把這個函數先聲明爲generator。異步編程

from greenlet import greenlet

def test1():
    print(12)
    gr2.switch()
    print(34)
    gr2.switch()

def test2():
    print(56)
    gr1.switch()
    print(78)

gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()


執行結果:
12
56
34
78

可是這個模塊沒有解決一個問題,就是自動切換IO操做,上面的切換是咱們人爲手動切換的。函數

Gevent

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

import gevent

def func1():
    print('\033[31;1m我是func1的1\033[0m')
    gevent.sleep(2)
    print('\033[31;1m我是func1的2\033[0m')

def func2():
    print('\033[32;1m我是func2的1\033[0m')
    gevent.sleep(1)
    print('\033[32;1m我是func2的2\033[0m')

def func3():
    print('\033[32;1m我是func3的1\033[0m')
    gevent.sleep(0)
    print('\033[32;1m我是func3的2\033[0m')

gevent.joinall([
    gevent.spawn(func1),
    gevent.spawn(func2),
    gevent.spawn(func3),
])


輸出結果:
我是func1的1
我是func2的1
我是func3的1
我是func3的2
我是func2的2
我是func1的2
相關文章
相關標籤/搜索