協程(coroutine)可以理解為是執行緒的優化,又稱之為輕量級進程。它是一種比執行緒更節省資源、效率更高的系統排程機制。
協程具有這樣的特點,即在同時開啟的多個任務中,一次只執行一個,只有當前任務遭遇阻塞,才會切換到下一個任務繼續執行。這種機制可以實現多工的同步,又能夠成功地避免執行緒中使用鎖的複雜性,簡化了開發。
早先的協程是使用生成器關鍵字 yield 來實現的,程式碼特別複雜難懂。自從 Python 3.5之後,確定了協程的語法,使得建立協程的方式得到改善。Python 中,能夠實現協程的模組有多個,如 asyncio、tornado 或 gevent。
這裡以 asyncio 為例,先來了解一下建立協程所用到的概念:
-
event_loop(事件迴圈):是一個協程處理常式的呼叫機制。程式會開啟一個無限迴圈,當事件發生時,呼叫相應的協程函數。
-
coroutine(協程物件):指一個使用 async 關鍵字來定義的函數。呼叫該函數,會返回一個協程物件。該協程物件就是一個處於掛起狀態的協程函數,需要註冊到事件迴圈 event_loop 中,由事件迴圈 event_loop 進行呼叫。
-
task 任務:是對協程的進一步封裝。
-
future:等同於 task,代表執行任務的結果。
-
async/await 關鍵字:Python 3.5 中用於定義協程的關鍵字,其中 async 用於定義一個協程,await 用於掛起阻塞的非同步呼叫介面。
Python協程的基本用法
下面通過程式碼演示最基本的協程用法:
# 匯入asyncio模組
import asyncio
#定義協程處理常式
async def demo(x):
print(x)
r = await asyncio.sleep(1)
print(x,"again")
#生成協程物件,並傳入 hello
coroutine = demo("C語言中文網")
loop = asyncio.get_event_loop()
try:
#將協程註冊到實現事件迴圈物件中,並開始執行。
loop.run_until_complete(coroutine)
finally:
#程式結束關閉事件迴圈物件
loop.close()
執行結果為:
C語言中文網
C語言中文網 again
注意,以上程式程式碼只能用在 Python 3.5 以及後續版本。因為 async 和 await 關鍵字是 3.5 版本才新加入。並且,在 Python 3.7 版本及後續版本中,可以使用 asyncio.run(coroutine) 代替第10~16行的程式碼,它們是完全等價的。
上面的程式碼體現了實現協程的基本步驟:
-
使用 async 關鍵字定義協程處理常式;
-
生成協程物件;
-
呼叫 asyncio 中的 get_event_loop 函數獲得事件迴圈物件;
-
呼叫事件迴圈物件的 run_until_complete 方法,執行協程處理常式。
協程VS執行緒
與執行緒相比,協程封裝的內容更多,實現起來更容易。
開發者使用協程會少考慮很多事情。在預設情況下應優先使用協程。但是有一個特點要注意,協程是多工的順序執行,只有當前任務掛起後,才會切換到其他任務來執行;而執行緒是以 CPU 輪換的方式執行。
因此,兩者的執行機制有著本質上的區別,這使得二者都不可被代替。也就是說,如果業務需求要每個並行執行緒的處理進度也要同步,那麼使用協程將不是一個很好的方案。在實際工作中,應根據業務需要來靈活運用執行緒或協程。