# loop.run_until_complete(coroutine)
# coroutine是協程的意思,但其意義在下面會介紹,目前只要把他理解成一個任務就行了
# 這個函數顧名思義,就是讓註冊參數裡的任務並執行,等到任務完成就關閉Event Loop
# loop.run_forever()
# 這個函數一執行,Event Loop就會永遠執行不會被關閉,除非在程式中出現loop.stop()就停止
定義協程(coroutin)
先解釋協程的意義,協程可以看做是"能在中途中斷、中途返回值給其他協程、中途恢復、中途傳入參數的函數",和一般的函數只能在起始傳入參數,不能中斷,而且最後返回值給父函數之後就結束的概念不一樣。
定義協程很簡單,只要在定義函數時再前面加入"async"這個關鍵字就行了:
# python3.5
# ubuntu 16.04
import asyncio
loop = asyncio.get_event_loop() #建立一個Event Loop
async def example(): # 定義一個協程
print("Start example coroutine.")
# do some process...
print("Finish example coroutine.")
loop.run_until_complete(example())
# 把example這個coroutine註冊到事件循環裡
# 被註冊進去的事件的定義是loop一啟動就直接觸發的,所以example裡面的任務內容會馬上執行
# output:
# Start example coroutine.
# Finish example coroutine.
但以上的例子無法體會到為何協程可以"中途中斷、中途恢復執行",下個例子我們可以在協程裡加入asyncio.sleep函數讓他中途暫停執行,並將執行權轉到其他的函數,之後在恢復執行:
# python3.5
# ubuntu 16.04
import asyncio
loop = asyncio.get_event_loop() #建立一個Event Loop
async def example1(): # 定義一個中間會被中斷的協程
print("Start example1 coroutin.")
await asyncio.sleep(1) # 中斷協程一秒
print("Finish example1 coroutin.")
async def example2(): # 定義一個協程
print("Start example2 coroutin.")
# do some process...
print("Finish example2 coroutin.")
tasks = [ # 建立一個任務列表
asyncio.ensure_future(example1()),
asyncio.ensure_future(example2()),
loop.run_until_complete(asyncio.wait(tasks))
# 把example1, example2這兩個coroutin註冊到事件循環裡
# loop啟動,先執行example1,中途暫停example1之後切換到example2,最後再切回example1
# output:
# Start example1 coroutin.
# Start example2 coroutin.
# Finish example2 coroutin.
# Finish example1 coroutin.
從output資訊可以看出example1是有被中斷執行的但上面的範例多了幾個需要解釋的東西:
asyncio.ensure_future(example1())
這函數把協程對象封裝成一個task對象,這個對象可以儲存任務執行時的狀態與環境,通常大家會說這對象儲存了任務的"上下文"(context),因為有task對象存在,協程才能順利的暫停與恢復執行,因為暫停時的變數內容都存在task對象裡,就像是遊戲存檔一樣。
2020/05/24更新
協程對象本身就能儲存任務的上下文,但經過task對象的包裝才能被Event Loop執行,所以說task對象負責作為Event Loop和協程對象的溝通介面。
協程是任務的實際內容,而task對象的作用是:
管理這個任務目前處在什麼狀態(ex. pending(等待完成)或是finished(執行完成)或是cancelled(已被取消)),並在為Event Loop和協程對象溝通時做出相應行為
當協程必須中途暫停時(像example裡面的await asyncio.sleep(1)),task對象會進行結尾會說明的Event:Callback註冊流程,確保異步模式正常運行
其實run_until_complete也是把協程先包裝成task對象後才註冊到Event Loop並執行的。
asyncio.wait(tasks)
這函數的用處在於把兩個example1和example2的兩個協程對象包成一個大的協程對象,就是把兩個小任務包成一個大任務。
await asyncio.sleep(1)
asyncio.sleep(1)簡單來說就是啟動一個只有一秒的倒數計時器,比較需要解釋的是await這個關鍵字。
如果沒有加入await的話,那Event Loop只會把計時器放在那邊跑,就不會讓example1暫停了。
如果要讓await這個概念跟上一篇的異步程式概念做一個銜接的話,example1在await之前的部份就像是Callback_B,當遇到await asyncio.sleep(1),Callback_B就會註冊一個"Event_D:Callback_D"進去,而Event_D就是倒數計時器倒數結束,Callback_D就是重新啟動example1在await之後的部份內容。
下一篇教學:
python的asyncio模組(四):Event loop常用API
參考資料:
不錯的asyncio語法解析
https://www.jianshu.com/p/b5e347b3a17c