在說明asyncio的基本用法之前,先來說明一下異步程式設計的幾項基本概念:
- 事件迴圈(event loop)
- 事件(event)
- 回調函數(callback)
先來思考一下為什麼異步程式設計為什麼要存在:
- 有多個任務需要同時執行
多個任務可以同時進行才符合一般人使用電腦的需求,假設我今天打開電腦,我想要一邊聽音樂一邊看網頁,播放音樂是一個任務,顯示網頁又是一個任務,若任務無法同時進行的話,難道要先把音樂放完才能看網頁嗎?
雖然現在CPU是多核心,但打開工作管理員會發現任務實在太多了,所以通常一核心的CPU要同時處理多件事情,那就只能讓任務各種穿插執行了,讓人腦感覺好像是同時進行一樣,像下面這張圖:

io調度時間比起CPU執行指令的時間實在是太長了
從上一篇文章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_A:Callback_A”
“Event_B:Callback_B”
“Event_C:Callback_C”
然後Event loop會如以下的方式循環的對list裡面的事件做監聽:
沒錯,異步程式設計應該要在遇到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”前”所要執行的指令。