python 協程(單線程中的異步調用)(轉廖雪峯老師python教程)

協程,又稱微線程,纖程。英文名Coroutine。css

協程的概念很早就提出來了,但直到最近幾年纔在某些語言(如Lua)中獲得普遍應用。python

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

因此子程序調用是經過棧實現的,一個線程就是執行一個子程序。函數

子程序調用老是一個入口,一次返回,調用順序是明確的。而協程的調用和子程序不一樣。性能

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

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

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的,因此協程的調用比函數調用理解起來要難一些。code

看起來A、B的執行有點像多線程,但協程的特色在因而一個線程執行,那和多線程比,協程有何優點?協程

最大的優點就是協程極高的執行效率。由於子程序切換不是線程切換,而是由程序自身控制,所以,沒有線程切換的開銷,和多線程比,線程數量越多,協程的性能優點就越明顯。

第二大優點就是不須要多線程的鎖機制,由於只有一個線程,也不存在同時寫變量衝突,在協程中控制共享資源不加鎖,只須要判斷狀態就行了,因此執行效率比多線程高不少。

由於協程是一個線程執行,那怎麼利用多核CPU呢?最簡單的方法是多進程+協程,既充分利用多核,又充分發揮協程的高效率,可得到極高的性能。

Python對協程的支持還很是有限,用在generator中的yield能夠必定程度上實現協程。雖然支持不徹底,但已經能夠發揮至關大的威力了。(gevent)

來看例子:

傳統的生產者-消費者模型是一個線程寫消息,一個線程取消息,經過鎖機制控制隊列和等待,但一不當心就可能死鎖。

若是改用協程,生產者生產消息後,直接經過yield跳轉到消費者開始執行,待消費者執行完畢後,切換回生產者繼續生產,效率極高:

import time def consumer(): r = '' while True: n = yield r if not n: return print('[CONSUMER] Consuming %s...' % n) time.sleep(1) r = '200 OK' def produce(c): c.next() n = 0 while n < 5: n = n + 1 print('[PRODUCER] Producing %s...' % n) r = c.send(n) print('[PRODUCER] Consumer return: %s' % r) c.close() if __name__=='__main__': c = consumer() produce(c) 

執行結果:

[PRODUCER] Producing 1... [CONSUMER] Consuming 1... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 2... [CONSUMER] Consuming 2... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 3... [CONSUMER] Consuming 3... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 4... [CONSUMER] Consuming 4... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 5... [CONSUMER] Consuming 5... [PRODUCER] Consumer return: 200 OK 

注意到consumer函數是一個generator(生成器),把一個consumer傳入produce後:

  1. 首先調用c.next()啓動生成器;

  2. 而後,一旦生產了東西,經過c.send(n)切換到consumer執行;

  3. consumer經過yield拿到消息,處理,又經過yield把結果傳回;

  4. produce拿到consumer處理的結果,繼續生產下一條消息;

  5. produce決定不生產了,經過c.close()關閉consumer,整個過程結束。

整個流程無鎖,由一個線程執行,produce和consumer協做完成任務,因此稱爲「協程」,而非線程的搶佔式多任務。

最後套用Donald Knuth的一句話總結協程的特色:

「子程序就是協程的一種特例。」

相關文章
相關標籤/搜索