相关文章推荐
腹黑的铁板烧  ·  JavaScript ...·  2 周前    · 
火星上的西瓜  ·  Fabric JS ...·  9 月前    · 
爱搭讪的芒果  ·  带你学MySQL系列 | ...·  1 年前    · 
瘦瘦的自行车  ·  Visual Studio 2022 ...·  1 年前    · 
# 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