玩轉python(6)協程
多任務系統一般都需要解決一個問題:多個任務如何調度。搶占式調度就是一種很常見的任務調度機制。以單核模式下的進程調度為例,一個進程處於運行狀態,其他的處於就緒隊列,等到當前運行的進程放棄CPU的使用權,系統將CPU立刻分配給新到達的進程,由於任務的執行順序是不確定的,看上去就像一堆任務在競爭CPU的使用權,所以這種多任務運行方式叫做“多任務競爭”。與之對應的是非搶占式調度。當前任務會持續執行下去直到因為某些原因主動放棄CPU的使用權,各個任務的執行順序是確定的,就像在互相協作,所以這種多任務運行方式也叫做“多任務協作”。協程就是一種典型的多任務協作解決方案。協程是指一個過程,這個過程與調用方協作,產出由調用方提供的值。它的運行流程大致如下:
- 協程A開始執行。
- 協程A執行到某一步,暫停執行,執行權轉移到協程B。
- 協程B執行一段時間後交還執行權。
- 協程A恢復執行。
那麽生成器是如何與協程扯上關系的呢?在一個生成器中,yield item這行代碼會產出一個值,提供給next()的調用方;此外,還會作出讓步,暫停執行生成器,讓調用方繼續工作,直到需要使用另一個值時再調用next()。調用方會從生成器中拉取值。如果只能產出值,生成器顯然用處不大。在python2.5之後的版本中,生成器API 中增加了send()方法。生成器的調用方可以使用send()方法發送數據,發送的數據會成為生成器函數中yield表達式的值。這樣生成器既能接收數據,又能產出數據,可以作為協程使用。下面是一個簡單的協程實例:
def fun():
r = ‘‘
while True:
n = yield r
if not n:
return
print(‘fun test %s...‘ % n)
r = ‘200 OK‘
協程在執行到n = yield r這個表達式時,首先輸出r,然後將接收到的數據賦給n。然後向下執行直到遇到下一個yield語句,循環往復。接下來我們來看看這個協程是怎麽工作的~
c = fun()
next(c)
c.send(‘1‘)
測試結果如下:
input:1
盡管fun()中有一個死循環,但是遇到yield,協程就會讓出執行權。而且協程有個亮點,它沒有涉及到上下文切換,所有的過程都是在用戶空間進行的,協程的創建開銷也很小,所以完全沒有創建線程時的各種考慮。因此如果有多個任務,並且我們知道如何安排任務協作的情況下,協程無疑是一個好選擇。
玩轉python(6)協程