在說明asyncio的基本用法之前,先來說明一下異步程式設計的幾項基本概念:
先來思考一下為什麼異步程式設計為什麼要存在:
多個任務可以同時進行才符合一般人使用電腦的需求,假設我今天打開電腦,我想要一邊聽音樂一邊看網頁,播放音樂是一個任務,顯示網頁又是一個任務,若任務無法同時進行的話,難道要先把音樂放完才能看網頁嗎?
雖然現在CPU是多核心,但打開工作管理員會發現任務實在太多了,所以通常一核心的CPU要同時處理多件事情,那就只能讓任務各種穿插執行了,讓人腦感覺好像是同時進行一樣,像下面這張圖:
來源網址:https://laike9m.com/blog/huan-zai-yi-huo-bing-fa-he-bing-xing,61/
從上一篇文章python的asyncio模組(一):異步執行的好處就能知道io時間比CPU執行時間要花費更久,所以為了不要讓CPU去等待io而浪費時間,我們就可以趁這段時間去切換執行其他任務,比如說播放音樂,從本地端或是網路上讀取音樂檔就是需要調動io,這時就可以利用中間小段空閒時間去開啟並顯示網頁,等到io調動時間結束,就可以重新執行原本任務了。
一、事件迴圈(Event loop)
既然異步程式可以在多個任務之間切換,那想必這個程式的架構裡一定有一個任務的list,這樣程式才知道有什麼樣的任務可以做切換,而這個任務的list以及中間切換的機制,就是由事件迴圈(Event loop)來處理。
二、事件(Event)與回調函數(Callback)
現在Event loop裏面有一個list,若程式有一些任務需要以異步的方式去執行,那就需要以"Event:Callback"的型式註冊進我們Event loop的list裏面,之後Event loop以for迴圈的方式去察看list裏面的Event是否發生,若發生了就執行相對應的Callback,並註銷這個Event的監聽。
打個比方,我今天對Event loop註冊了三個任務:
然後Event loop會如以下的方式循環的對list裡面的事件做監聽:
假設正在監聽的Event_B發生了,就會註銷Event_B的監聽(也就是把Event_B從list中移除),然後執行Callback_B,執行完後在繼續監聽剩下的事件。
但上圖的解釋可能會出現一個疑問,裡面的意思是執行完Callback_B之後才會繼續監聽,但假設在執行Callback_B這個任務的中途,出現了需要調動io的指令,不就應該要先放下Callback_B這個任務,去監聽其他事件並執行其他任務嗎?為什麼要等到Callback_B執行完呢?
沒錯,異步程式設計應該要在遇到io讀取的時候切換其他任務去執行,但上圖確實是Event_loop的實作機制,所以如果你的Callback_B的內容像以下這樣:
def Callback_B():
do_some_work1()
read_from_io_and_wait() # 讀取io的指令,等到讀取完成才能執行下一個指令
do_some_work2()
return
你的程式是沒有辦法中途去切換其他任務的,若要能夠切換其他任務,應該要設計成以下型式:
def Callback_B():
do_some_work1()
read_from_io_and_not_wait() # 讀取io的指令,不用等到讀取完成就直接執行下一個指令
register_to_EventLoop("finish read from io",Callback_D)
return
def Callback_D():
# 等到Callback_B讀取io的指令完成並被EventLoop監聽到就執行
do_some_work2()
return
事實上如果Callback_B若要有遇到調動io指令就暫停執行的功能,那他應該要設計成遇到調動io指令,就多註冊一個"Event_D:Callback_D"進去Event loop list,這個Event_D指的是"調動io完成",Callback_D的任務範圍是完成調動io"後"所要執行的指令,而Callback_B的任務範圍應該只有完成調動io"前"所要執行的指令。
下一篇教學:
python的asyncio模組(三):建立Event Loop和定義協程
參考資料:
Event loop的實作細節
http://lotabout.me/2017/understand-python-asyncio/
最後一張圖, 最右邊的 Event Loop ListEvent_B
應該換成 Event_C
?
是的 感謝你 已經改過來了