古詩詞大全網 - 成語用法 - Python協程之asyncio

Python協程之asyncio

asyncio 是 Python 中的異步IO庫,用來編寫並發協程,適用於IO阻塞且需要大量並發的場景,例如爬蟲、文件讀寫。

asyncio 在 Python3.4 被引入,經過幾個版本的叠代,特性、語法糖均有了不同程度的改進,這也使得不同版本的 Python 在 asyncio 的用法上各不相同,顯得有些雜亂,以前使用的時候也是本著能用就行的原則,在寫法上走了壹些彎路,現在對 Python3.7+ 和 Python3.6 中 asyncio 的用法做壹個梳理,以便以後能更好的使用。

協程,又稱微線程,它不被操作系統內核所管理,而完全是由程序控制,協程切換花銷小,因而有更高的性能。

協程可以比作子程序,不同的是,執行過程中協程可以掛起當前狀態,轉而執行其他協程,在適當的時候返回來接著執行,協程間的切換不需要涉及任何系統調用或任何阻塞調用,完全由協程調度器進行調度。

Python 中以 asyncio 為依賴,使用 async/await 語法進行協程的創建和使用,如下 async 語法創建壹個協程函數:

在協程中除了普通函數的功能外最主要的作用就是:使用 await 語法等待另壹個協程結束,這將掛起當前協程,直到另壹個協程產生結果再繼續執行:

asyncio.sleep() 是 asyncio 包內置的協程函數,這裏模擬耗時的IO操作,上面這個協程執行到這壹句會掛起當前協程而去執行其他協程,直到sleep結束,當有多個協程任務時,這種切換會讓它們的IO操作並行處理。

註意,執行壹個協程函數並不會真正的運行它,而是會返回壹個協程對象,要使協程真正的運行,需要將它們加入到事件循環中運行,官方建議 asyncio 程序應當有壹個主入口協程,用來管理所有其他的協程任務:

在 Python3.7+ 中,運行這個 asyncio 程序只需要壹句: asyncio.run(main()) ,而在 Python3.6 中,需要手動獲取事件循環並加入協程任務:

事件循環就是壹個循環隊列,對其中的協程進行調度執行,當把壹個協程加入循環,這個協程創建的其他協程都會自動加入到當前事件循環中。

其實協程對象也不是直接運行,而是被封裝成壹個個待執行的 Task ,大多數情況下 asyncio 會幫我們進行封裝,我們也可以提前自行封裝 Task 來獲得對協程更多的控制權,註意,封裝 Task 需要 當前線程有正在運行的事件循環 ,否則將引 RuntimeError,這也就是官方建議使用主入口協程的原因,如果在主入口協程之外創建任務就需要先手動獲取事件循環然後使用底層方法 loop.create_task() ,而在主入口協程之內是壹定有正在運行的循環的。任務創建後便有了狀態,可以查看運行情況,查看結果,取消任務等:

asyncio.create_task() 是 Python3.7 加入的高層級API,在 Python3.6,需要使用低層級API asyncio.ensure_future() 來創建 Future,Future 也是壹個管理協程運行狀態的對象,與 Task 沒有本質上的區別。

通常,壹個含有壹系列並發協程的程序寫法如下(Python3.7+):

並發運行多個協程任務的關鍵就是 asyncio.gather(*tasks) ,它接受多個協程任務並將它們加入到事件循環,所有任務都運行完成後會返回結果列表,這裏我們也沒有手動封裝 Task,因為 gather 函數會自動封裝。

並發運行還有另壹個方法 asyncio.wait(tasks) ,它們的區別是: