理解Python異步中的事件循環(asyncio管理事件循環)

  • Python中的異步編程

    Python實現異步編程的方案有:

    • twisted:使用事件驅動機制來提升python性能
    • gevent:使用greenlet在用戶態完成棧和上下文切換來減少切換帶來的性能損耗
    • tornado:使用生成器來保存協程上下文及狀態,使用原生的python語法實現了協程
    • asyncio:異步編程標準化。
  • Python異步編程的特點

    • 單線程、異步阻塞
    • 異步的實現基於協程,不僅使得Python中的異步是阻塞的,而且最小執行單位不再是單個任務,而是每個任務的一部分。
  • Python高併發方案的區別

    多進程、多線程這類方案,IO的調度取決於系統;

    協程,調度來自於用戶。

    asyncio模塊實現異步,也是採用協程實現。

  • asyncio模塊的主要組件和概念

    • 事件循環:asyncio模塊中,每一個進程都有一個事件循環。程序員把一些函數註冊到事件循環上,當滿足事件發生的時候,調用相應的協程函數;
    • 協程:子程序的泛化概念,可以在執行期間暫停,等待外部的處理完成之後,從之前暫停的地方回覆執行。理解爲一個使用async關鍵字定義的函數。它的調用不會立即執行,而是會返回一個協程對象,協程對象需要註冊到事件循環,由事件循環調用
    • Futures:定義Future對象,表示尚未完成的計算;
    • Tasks:用於封裝和管理並行模式下的協程;協程對象是一個可以掛起的函數,任務則是對協程進一步封裝,其中包含任務的各種狀態;
    • async/await關鍵字:定義協程的關鍵字,async定義一個協程,await用於掛起阻塞的異步調用接口。
  • 事件循環Event loop(維基百科

    事件的本質就是異步。

    事件源:可以產生事件的實體;

    事件處理者:能處理事件的實體;

    事件循環:第三方實體。

    事件循環的作用是管理所有的事件,在整個程序運行過程中不斷循環執行,追蹤事件發生的順序將它們放到隊列中,當主線程空閒的時候,調用相應的事件處理者處理事件。

  • 協程與事件循環案例

    import asyncio
    import time
    
    async def do_some_work(x):  # async關鍵字定義一個協程
        print('Waiting:', x) 
    coroutine = do_some_work(2)   # 協程的調用不會直接執行,而是返回一個協程對象
    
    loop = asyncio.get_event_loop()  # 創建一個事件循環
    loop.run_until_complete(coroutine) # run_until_complete將協程註冊到事件循環,並啓動事件循環。
    
    

    協程對象不能直接運行,在註冊事件循環時,run_until_complete將協程包裝成了一個tasktask對象是Futurn類的子類,保存了協程運行後的狀態,用於未來獲取協程的結果。

    import asyncio
    import time
    
    async def do_some_work(x):  # async關鍵字定義一個協程
        print('Waiting:', x) 
    coroutine = do_some_work(2)   # 協程的調用不會直接執行,而是返回一個協程對象
    
    loop = asyncio.get_event_loop()  # 創建一個事件循環
    
    task = loop.create_task(coroutine) # 創建task,此時的task尚未加入事件循環,狀態爲pending
    loop.run_until_complete(task) # run_until_complete將task註冊到事件循環,並啓動事件循環。
    # task執行完後,狀態爲finished
    
    

    run_until_complete參數是一個future對象,當傳入的是:

    • 協程,會自動封裝成task
    • tasktaskFuture的子類。asyncio.ensure_future(coroutine)loop.create_task(coroutine)都可以創建task
    # 綁定回調函數
    import asyncio
    import time
    
    async def do_some_work(x):  # async關鍵字定義一個協程
        print('Waiting:', x) 
        return 'Done after {}s'.format(x)  # 返回值
    def callback(future): # 定義一個回調函數,最後一個參數是future對象,如果這裏有多個參數,下方通過偏函數導入
        print('Callback: ', future.result()) # 返回future的結果
        
    coroutine = do_some_work(2)   # 協程的調用不會直接執行,而是返回一個協程對象
    
    loop = asyncio.get_event_loop()  # 創建一個事件循環
    
    task = loop.create_task(coroutine) # 創建task,此時的task尚未加入事件循環,狀態爲pending
    task.add_done_callback(callback) # 綁定回調函數,在task執行完畢的時候獲取執行的結果
    loop.run_until_complete(task) # run_until_complete將task註冊到事件循環,並啓動事件循環。
    # task執行完後,狀態爲finished
    
    

    coroutine執行結束時會調用回調函數,並通過參數future獲取協程執行的結果,此future是沒有傳入的,實際上與創建的task是同一個對象

    # 阻塞和await
    import asyncio
    import time
    
    now = lambda: time.time()
    
    async def do_some_work(x):
        print('Waiting: ', x) 
        await asyncio.sleep(x) # 執行sleep時,await使此協程主動讓出控制權,loop調用其他協程
        return 'Done after {}s'.format(x)
    
    start = now()
    
    coroutine = do_some_work(2)
    loop = asyncio.get_event_loop()
    task = asyncio.ensure_future(coroutine)
    loop.run_until_complete(task)
    
    print('Task ret: ', task.result())
    print('TIME: ', now() - start)  
    # 以上還只有一個協程,讓出控制權也暫時無用,如果有多個協程,協程A讓出控制權,loop就會把控制權分配給協程B,實現了併發。
    # asyncio實現併發:每當一個協程任務阻塞的時候就await,讓後其他協程繼續工作。
    import asyncio
    
    import time
    
    now = lambda: time.time()
    
    async def do_some_work(x):
        print('Waiting: ', x)
    
        await asyncio.sleep(x)
        return 'Done after {}s'.format(x)
    
    start = now()
    
    coroutine1 = do_some_work(1)
    coroutine2 = do_some_work(2)
    coroutine3 = do_some_work(4)
    
    tasks = [
        asyncio.ensure_future(coroutine1),
        asyncio.ensure_future(coroutine2),
        asyncio.ensure_future(coroutine3)
    ]
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))   # asyncio.wait(接收task列表);asyncio.gather(接收多個task)
    
    for task in tasks:
        print('Task ret: ', task.result())
    
    print('TIME: ', now() - start)
    
  • How the heck does async/await work in Python 3.5?

    From Wikipedia, an eventloop is a programming construct that waits for and dispathches enents or messages in a program.Basically an event loop lets you go, “When A happens, do B”.

    In Python’s case,asyncio was added to the standard library to provide an event loop.

  • Reference

  1. 線程、事件與QObject
  2. Microsoft Windows的消息循環
  3. python協程進階,原來實現一個事件循環可以如此簡單!
  4. Python並行編程 中文版
  5. Python 協程和事件循環
  6. 從0到1,Python異步編程的演進之路
  7. 11.6. asyncio — 異步 I/O,事件循環和併發工具
  8. Python黑魔法 — 異步IO( asyncio) 協程
  9. How the heck does async/await work in Python 3.5?
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章