第三次理解asyncio

  • asynchronous(異步)/concurrent(併發)/multiprocess(多進程) in Python

    相關模塊有:

    1. _thread
    2. threading (對_thread的高級封裝)
    3. multiprocessing
    4. concurrent.futures(多threading/multiprocessing的高級封裝)
    5. asyncio
  • asyncio

    參考資料:

    1. Official Document
    2. 廖雪峯的官方網站
    3. Guide to Concurrency in Python with Asyncio
    4. Python黑魔法 — 異步IO( asyncio) 協程
    5. 且聽風吟
    6. 小明
    7. 理解 Python asyncio Python Generator

    asyncio is a library to write concurrent(併發) code using the async/await syntax.

    asyncio is used as a foundation for multiple Python asynchronous(異步) frameworks that provide high-performance network and web-servers, database connection libraries, distributed task queues, etc.

    asyncio模塊有四個基本概念:

    1. eventloop
    2. coroutine
    3. future
    4. task

    Python3.4 增加了asyncio模塊支持異步IO,起初使用@asyncio.coroutine || yield from語法,Python3.5後使用async || await語法替代。

  • 異步IO asynchronous|| 併發 concurrency || 並行 parallel1

    所謂異步IO,就是當發起一個IO操作,卻不等它結束,直接去做其他事情,當它結束時,會通知你。

    同步IO模型的代碼無法實現異步IO模型。異步IO模型需要有一個eventloop,在eventloop中主線程不斷重複“讀取消息–> 處理消息”這一過程。

    所謂併發(concurrency),是指同一時刻平等的運行不同事情。多進程、多線程都可以實現。是一種邏輯上的假相。不同任務邏輯上同時運行,但處理器只有一個,同一時刻處理器上只能處理一個任務。核心是交替,邏輯上。

    所謂並行(parallel),是真正的同時運行,同一時刻多個任務在多個處理器上同時執行。核心是並列,物理上。

    併發指的是程序的結構,並行指的是程序運行時的狀態。

  • 概念一:事件循環 event loop

    功能類似於CPU(順序執行協程代碼)、操作系統(完成協程調度)。

    作爲中央總控,eventloop實例提供了註冊/取消/執行任務(和回調)的方法。

    把一些列異步函數註冊到事件循環上,事件循環就會循環執行這些函數(同一時刻只能執行一個),當執行到某個函數正在等待I/O返回,事件循環就會暫停它的執行去執行其他函數;當某個函數完成I/O後會恢復,下次循環到它的時候繼續執行。

    eventloop機理上,遇到IO操作時,代碼只發出IO請求,不等待IO結果,直接結束本輪消息處理,進入下一輪消息處理過程。

    當IO操作完成後,收到一條“IO完成”的消息,處理該消息時就可以直接獲取IO操作結果。

    在“發出IO請求”到收到“IO完成”這段時間內,主線程繼續處理eventloop中其他消息,如此異步IO模型下,一個線程就可以同時處理多個IO請求,而不用產生線程切換的消耗。

    The eventloop is what schedules and runs asynchronous tasks.

  • uvloop

    According to the authors of uvloop, it is comparible in speed to that of Go programs.

    If you want to utilize uvloop the first install it ,the add a call to uvloop.install()

    import asyncio
    import uvloop
    
    async def foo():
        print("Foo!")
    
    async def hello_world():
        await foo()
        print("Hello World!")
    
    uvloop.install() # 使用uvloop替換原生eventloop
    asyncio.run(hello_world())
    
  • 概念二:協程 coroutine

    coroutine本質上是一個函數,在代碼塊中可以由用戶將執行權交給其他協程。

    協程可以做哪些事2

    1. 等待一個future結束
    2. 等待另一個協程(產生一個結果,或引發一個異常) await asyncio.sleep(x)
    3. 產生一個結果給正在等它的協程
    4. 引發一個異常給正在等它的協程
    • function or object
      1. coroutine function : an async def function;
      2. coroutine object : an object returned by calling a coroutine function
    • 協程的運行

      調用協程函數,協程並不會開始運行,只是返回一個協程對象。

      運行協程對象的兩種方式:

      1. 在另一個已經運行的協程中用“await”等待它
      2. 通過“ensure_future”函數計劃它的執行

      本質上,只有loop運行了,協程纔會運行。

      import asyncio
      
      async def do_some_work(x):  # async 用於定義一個coroutine對象
      	print("waiting " + str(x))
      	await asyncio.sleep(x) # await調用另一個coroutine對象
          print("again") # asycnio.sleep()相當於IO,這裏並不是等待IO結束,而是直接中斷do_some_work()這個coroutine,當IO完成後才重新啓動本協程
      
      loop = asyncio.get_event_loop() # 定義一個無協程的loop
      loop.run_until_complete(do_some_work(3)) # 把協程對象加入到loop,此處參數應爲future對象,只是內部可以自動檢測封裝
      loop.run_until_complete(asyncio.ensure_future(do_some_work(3))) # 正規寫法
      

      run_until_complete只接收單個future,如果有多個協程需要用syncio.gather([coroutine1,c2,…]),asyncio.gather()本質是併發運行任務.

      loop = asyncio.get_event_loop()
      loop.run_until_complete(main())
      loop.close()
      # 等價於
      asyncio.run(main())
      

      The .run function always creates a new event loop and closes it at the end. It’s a high-level API.

    • 協程的併發:asyncio.gather || asyncio.wait
      return_a, return_b = await asyncio.gather(a(), b())
      done, pending = await asyncio.wait([a(), b()])
      # return_a 等價於list(done)[0].result()
      

      兩者都是讓多個協程併發執行,asyncio.gather()會按照輸入協程的順序保存對應協程的執行結果。

      asyncio.wait()返回封裝的task(已完成的和被掛起的任務),如果需要協程的執行結果就要從對應task實例中採用result方法拿到。

      asyncio.wait()接收一個參數return_when,表示返回的時機。可選有:

      • ALL_COMPLETED : 全部任務完成後返回
      • FIRST_COMPLETED : 第一個協程完成後返回
      • FIRST_EXCEPTION : 第一個異常出現後返回
    • 協程與函數

      在函數世界內,當一個函數中斷去執行另一個函數採用調用另一個函數;協程中,函數A執行中斷去執行函數B是由事件循環操作的,並不涉及函數之間的調用。

      不同協程是在同一個線程內切換的。

  • 概念三:Future

    future是coroutine的封裝,future對象提供了很多任務方法,可以向Future實例添加完成後的回調(add_done_callback)、取消任務(cancle)、設置最終結果(set_result)、設置一場(set_exception)等。

  • 概念四:Task

    task是future這種底層類的子類。實際使用中更過直接調用task而非future。

    Task的創建有三種方法:

    1. asyncio.create_task(coro) Python3.7後推薦用法,本質是對loop.create_task的封裝
    2. loop.create_task(coro)
    3. asyncio,ensure_future(coro/future/awaitable)
  • Awaitables

    Something is awaitable if it can be used in an await expression.

    There are three main tyoes of awaitables:

    1. Coroutine
    2. Tasks
    3. Futures(low-level type)
  • 回調

    可以給Task/Future添加回調函數,等task完成後就會自動調用這些回調函數。

    task.add_done_callback(callback)

    回調函數按其註冊順序被調用。

  • 同步代碼

    如果有同步邏輯需要執行,可以放在loop.run_in_executor(concurrent.futures.Executor實例,同步函數)裏執行。

  • References


  1. 還在疑惑併發和並行? ↩︎

  2. Python 的異步 IO:Asyncio 簡介 ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章