1. 程式人生 > >如何用python實現異步io

如何用python實現異步io

一輪 線程數 我們 由於 模型 負責 網絡數據 返回結果 file

在IO編程一節中,我們已經知道,CPU的速度遠遠快於磁盤、網絡等IO。在一個線程中,CPU執行代碼的速度極快,然而,一旦遇到IO操作,如讀寫文件、發送網絡數據時,就需要等待IO操作完成,才能繼續進行下一步操作。這種情況稱為同步IO。

在IO操作的過程中,當前線程被掛起,而其他需要CPU執行的代碼就無法被當前線程執行了。

因為一個IO操作就阻塞了當前線程,導致其他代碼無法執行,所以我們必須使用多線程或者多進程來並發執行代碼,為多個用戶服務。每個用戶都會分配一個線程,如果遇到IO導致線程被掛起,其他用戶的線程不受影響。

多線程和多進程的模型雖然解決了並發問題,但是系統不能無上限地增加線程。由於系統切換線程的開銷也很大,所以,一旦線程數量過多,CPU的時間就花在線程切換上了,真正運行代碼的時間就少了,結果導致性能嚴重下降。

由於我們要解決的問題是CPU高速執行能力和IO設備的龜速嚴重不匹配,多線程和多進程只是解決這一問題的一種方法。

另一種解決IO問題的方法是異步IO。當代碼需要執行一個耗時的IO操作時,它只發出IO指令,並不等待IO結果,然後就去執行其他代碼了。一段時間後,當IO返回結果時,再通知CPU進行處理。

可以想象如果按普通順序寫出的代碼實際上是沒法完成異步IO的:

do_some_code()
f = open(‘/path/to/file‘, ‘r‘)
r = f.read() # <== 線程停在此處等待IO操作結果
# IO操作完成後線程才能繼續執行:
do_some_code(r)

所以,同步IO模型的代碼是無法實現異步IO模型的。

異步IO模型需要一個消息循環,在消息循環中,主線程不斷地重復“讀取消息-處理消息”這一過程:

loop = get_event_loop()
while True:
    event = loop.get_event()
    process_event(event)

消息模型其實早在應用在桌面應用程序中了。一個GUI程序的主線程就負責不停地讀取消息並處理消息。所有的鍵盤、鼠標等消息都被發送到GUI程序的消息隊列中,然後由GUI程序的主線程處理。

由於GUI線程處理鍵盤、鼠標等消息的速度非常快,所以用戶感覺不到延遲。某些時候,GUI線程在一個消息處理的過程中遇到問題導致一次消息處理時間過長,此時,用戶會感覺到整個GUI程序停止響應了,敲鍵盤、點鼠標都沒有反應。這種情況說明在消息模型中,處理一個消息必須非常迅速,否則,主線程將無法及時處理消息隊列中的其他消息,導致程序看上去停止響應。

消息模型是如何解決同步IO必須等待IO操作這一問題的呢?當遇到IO操作時,代碼只負責發出IO請求,不等待IO結果,然後直接結束本輪消息處理,進入下一輪消息處理過程。當IO操作完成後,將收到一條“IO完成”的消息,處理該消息時就可以直接獲取IO操作結果。

在“發出IO請求”到收到“IO完成”的這段時間裏,同步IO模型下,主線程只能掛起,但異步IO模型下,主線程並沒有休息,而是在消息循環中繼續處理其他消息。這樣,在異步IO模型下,一個線程就可以同時處理多個IO請求,並且沒有切換線程的操作。對於大多數IO密集型的應用程序,使用異步IO將大大提升系統的多任務處理能力。

如何用python實現異步io