現在asyncio在Python中的使用越來越廣泛了,但是很多人對于協程(corotine)的一些使用方式還不太熟悉。在這篇文章中,我將會介紹如何識別協程是否被block了,并以常用的HTTP網絡庫requests/httpx為例來說明如何避免協程被block的問題。
為什么協程會被block
在Python中,可以通過多協程的方式來實現并發,但是如果在協程中使用了阻塞的IO操作,那么這個協程就會被block,從而影響整個事件循環的執行。在這種情況下,雖然已經啟動了很多協程,但是其實所有的協程都被卡主沒有繼續執行,也就不能發揮其并發的優勢了。
一般來說,進行HTTP請求時,在Python中和asyncio配合的更多是httpx而非經典的requests庫。這些就是因為,在默認情況下,httpx的AsyncClient是不會block住協程的,而requests庫則是會block住協程。如果在多協程的情況下直接使用requests庫,其實就沒有發揮出任何協程的并發優勢。
如何識別協程是否被block
我們可以通過一個獨立的協程任務來檢查自己是否block中,從而發現當前的事件循環中是否有協程被block。這個任務的視線方式如下:
import time
import asyncioTHRESHOLD = 0.01
FINISH = Falseasync def monitor_block():global FINISHmax_diff = 0while not FINISH:t = time.time() await asyncio.sleep(1.0)diff = time.time() - t - 1.0if diff > max_diff:max_diff = diffif diff > THRESHOLD:print(f'block found, diff {diff}s')print(f'{max_diff=}')
這個任務會每秒檢查一次當前的時間是否和預期的時間相差太大,也就是asyncio.sleep
函數的執行是否收到的影響,我們設定一個閾值THRESHOLD,如果實際的sleep的時間超過閾值,我們就認為當前的協程被block了。
下面實際比較一下requests和httpx的區別: