Python 模塊 asyncio-異步IO,事件循環和並發

Python 模塊 asyncio-異步IO,事件循環和併發

模塊 asyncio 是一個異步IO和併發框架。

asyncio 提供了協程 coroutines 創建併發應用,它使用單線程,單進程的模式進行顯示的任務切換。大部分的任務切換都發生在可能會阻塞的地方,例如讀取文件或者網絡等等。asyncio 提供了一些特性包括在指定時間運行某個任務,指示某個 coroutines 等待其他的完成才開始執行等等。

模塊 threading 和 multiprocessing 分別使用多線程和多進程進行多任務的同步運行。

概念


大多數應用程序都是線性的開發,然後依賴語言底層的線程或者進程切換任務並行運行。基於 asyncio 開發的併發程序需要在程序中手動進行上下文的切換,因為它運行在單線程,單進程的模式上。下面是需要理解的一些概念。

asyncio 框架裡需要重點專注的是事件循環(event loop),它是處理事件(event)的一個主要對象,例如IO事件、系統事件、應用任務切換等等。

應用首先需要註冊(register)要運行的任務到事件循環中,當得到所需的資源後,已註冊的任務被事件循環喚醒執行。例如服務端程序當收到一個客戶端的請求或者有數據要讀取時再執行操作,當處理完成後,立刻把控制權交回給事件循環準備接受下一個事件。

控制器交回給事件循環依賴協程 coroutines,它是一個特殊的函數把控制器交回而不丟失狀態,這和 yield 非常類似。事實上,在 Python 3.5 之前要想實現協程,就要使用 yield 生成器函數。asyncio 提供了基於類的抽象層,可以直接寫回調方法而不用寫協程。

對象 Future 是一個表示結果的數據結構,asyncio 可以監控一個 Future 對象允許應用等待一項任務完成時返回。

Future 的子類 Task 知道怎麼管理一個協程的執行,Task 可以等待一個資源可用時,由事件循環調用。

協程 Coroutine


協程 Coroutine 是運行併發操作的一個語言結構,一個協程函數調用的時候就創建了一個攜程對象,然後調用對象的 send() 方法就會執行它定義的代碼。協程還可以使用 await 關鍵字暫停執行,暫停的時候不會丟失狀態,然後可以等待喚醒繼續執行。

運行協程


要讓一個事件循環運行協程,最簡單的方法是調用 run_until_complete(),參數傳遞一個協程對象。

Python 模塊 asyncio-異步IO,事件循環和併發

執行:

Python 模塊 asyncio-異步IO,事件循環和併發

本例使用 async 關鍵字放在函數 coroutine() 之前,代表這是一個協程函數。run_until_complete() 方法傳入協程對象,開始事件循環,直到協程對象退出後返回。最後使用 try:finally 確保最後關閉事件循環。

從協程返回值


run_until_complete() 可以返回協程的結果。

Python 模塊 asyncio-異步IO,事件循環和併發

執行:

Python 模塊 asyncio-異步IO,事件循環和併發

協程鏈


一個協程可以啟動另一個協程,並等待它的結果,這樣更容易把一個任務分解成多個可重用的部分。下面的例子展示了必須順序執行的兩個協程,但是和其他的協程可以併發的運行。

Python 模塊 asyncio-異步IO,事件循環和併發

執行:

Python 模塊 asyncio-異步IO,事件循環和併發

本例在協程 worker() 中,創建了兩個協程,使用關鍵字 await。因為控制流已經在事件循環中了,所以這裡創建的兩個協程也被事件循環管理。

協程調用普通函數


asyncio 在事件循環中還可以調用普通函數,如果對調用時間沒有要求,方法 call_soon() 會在事件循環的下次調用函數。

call_soon() 方法的第一個參數是函數引用,第二個參數是傳遞給函數的參數。如果需要傳遞多個參數,例如關鍵字參數,可以使用 functools 模塊的 partial() 函數。

Python 模塊 asyncio-異步IO,事件循環和併發

執行:

Python 模塊 asyncio-異步IO,事件循環和併發

延遲調用函數


使用方法 call_later() 延遲調用回調函數,第一個參數是要延遲的時間,單位是秒。

Python 模塊 asyncio-異步IO,事件循環和併發

執行:

Python 模塊 asyncio-異步IO,事件循環和併發

本例中,同樣的回調函數使用不同的參數調用了多次,call_soon() 方法會使用最小的延遲時間,所以它第一個執行。

指定的時間調用函數


有時候需要在指定的時間執行回調函數。事件循環使用的時鐘是 monotonic clock,而不是掛鐘時間 wall time。所以為了保證時間不會倒退,應該使用事件循環的時間,因為 wall time 是可以修改的。

monotonic clock 代表某個時間點自然流逝的時間,不受 time-of-day 時鐘修改的影響,例如你不想因為電腦重啟而影響時間的話,就應該使用它。

wall time 通常就是我們在電腦上看到的時間,可以手動修改也包括 NTP 對它的修改。(NTP: Network Time Protocol 是用來使網絡時間和本地時間同步的協議,它可以使服務器或時鐘源同步修改時間)

Python 模塊 asyncio-異步IO,事件循環和併發

執行:

Python 模塊 asyncio-異步IO,事件循環和併發


分享到:


相關文章: