<input id="ohw05"></input>
  • <table id="ohw05"><menu id="ohw05"></menu></table>
  • <var id="ohw05"></var>
  • <code id="ohw05"><cite id="ohw05"></cite></code>
    <label id="ohw05"></label>
    <var id="ohw05"></var>
  • python協程asyncio的個人理解

    協程與任務

    python語境中,協程 coroutine 的概念有兩個:協程函數、協程對象,協程對象由協程函數創建得到(類似于類實例化得到一個對象).

    理解協程,最重要的是了解事件循環和任務執行的機制,下面是三個原則:

    • 事件循環中,不斷循環執行各個任務,若一個任務遇到await或者執行完成,則返回控制權給事件循環,這時候事件循環再去執行下一個任務
    • 事件循環同一時刻只會運行一個任務
    • 協程不會被加入事件循環的執行日程,只有被注冊為任務之后,事件循環才可以通過任務來設置日程以便并發執行協程

    基本語法

    協程的聲明和運行

    使用async def語句定義一個協程函數,但這個函數不可直接運行

    async def aaa():
        print('hello')
    
    print(aaa())
    
    # 輸出----------------------------------
    <coroutine object aaa at 0x7f4f9a9dfec0>
    /root/Project/test01/test2.py:4: RuntimeWarning: coroutine 'aaa' was never awaited
      print(aaa())
    RuntimeWarning: Enable tracemalloc to get the object allocation traceback
    

    如何運行一個協程呢,有三種方式:

    1. 使用asyncio.run()函數,可直接運行
    import asyncio
    
    async def aaa():
        print('hello')
    
    asyncio.run(aaa())
    # 輸出-------------------------
    hello
    
    1. 使用await進行異步等待

    在協程函數中最重要的功能是使用await語法等待另一個協程,這將掛起當前協程,直到另一個協程返回結果。

    await的作用:掛起 coroutine 的執行以等待一個 awaitable 對象。 只能在 coroutine function 內部使用。

    import asyncio
    
    async def aaa():
        print('hello')
    
    async def main():
        await aaa()
    
    asyncio.run(main())
    
    1. 使用asyncio.create_task() 函數來創建一個任務,放入事件循環中
    import asyncio
    
    async def aaa():
        print('hello')
    
    async def main():
        asyncio.create_task(aaa())
    
    asyncio.run(main())
    

    可等待對象

    上面說過,協程函數中最重要的功能是使用await語法等待另一個協程,這將掛起當前協程,直到另一個協程返回結果。(重要,重復一遍)

    await后面需要跟一個可等待對象(awaitable),有下面三種可等待對象:

    • 協程:包括協程函數和協程對象
    • 任務:通過asyncio.create_task()函數將協程打包為一個任務
    • Futures:特殊的 低層級 可等待對象,表示一個異步操作的 最終結果

    運行asyncio程序

    asyncio.run(coro, ***, debug=False)

    傳入協程coroutine coro ,創建事件循環,運行協程返回結果,并在結束時關閉,應當被用作 asyncio 程序的主入口點。

    創建任務

    asyncio.create_task(coro, ***, name=None)

    將 coro 協程 打包為一個 Task 排入日程準備執行。返回 Task 對象。

    休眠

    coroutine asyncio.sleep(delay, result=None, ***, loop=None)

    阻塞 delay 指定的秒數,該協程總是會掛起當前任務,以允許其他任務運行

    機制解析

    通過官網的兩段代碼,來詳細解析一下協程的運行機制。

    官方兩個代碼如下,注意看輸出差異:

    代碼1,通過協程對象來執行

    import asyncio
    import time
    
    async def say_after(delay, what):
        await asyncio.sleep(delay)
        print(what)
    
    async def main():
        print(f"started at {time.strftime('%X')}")
    
        await say_after(1, 'hello')
        await say_after(2, 'world')
    
        print(f"finished at {time.strftime('%X')}")
    
    asyncio.run(main())				# 1: 創建事件循環,傳入入口點main()協程對象,此時生成一個對應的task
    

    輸出為:

    started at 17:13:52
    hello
    world
    finished at 17:13:55
    

    代碼2,通過任務來執行

    import asyncio
    import time
    
    async def say_after(delay, what):
        await asyncio.sleep(delay)
        print(what)
    
    async def main():
        task1 = asyncio.create_task(
            say_after(1, 'hello'))
    
        task2 = asyncio.create_task(
            say_after(2, 'world'))
    
        print(f"started at {time.strftime('%X')}")
    
        await task1
        await task2
    
        print(f"finished at {time.strftime('%X')}")
    
    asyncio.run(main())
    

    輸出:

    started at 17:14:32
    hello
    world
    finished at 17:14:34
    

    注意到運行時間比前一個代碼快1秒,下面說明為什么出現這種情況(文字比較多)。

    代碼一的運行邏輯:

    asyncio.run(main()) 啟動一個事件循環,將入口點main()協程對象傳入,生成一個對應的任務task_main;

    事件循環運行任務task_main,然后執行第1條代碼:print(f"started at {time.strftime('%X')}");

    接著執行第2條代碼:await say_after(1, 'hello'),第2條代碼首先生成一個say_after(1, 'hello')協程對象,同時生成該協程對象對應的task_1;

    由于await語法,task_main任務將控制權返回給事件循環,同時告訴事件循環需要等待task1才能繼續運行;

    事件循環獲得控制權后,發現此時有兩個任務task_main和task1,同時task_main在等待task1,于是會去執行task1任務;

    task1任務將執行第1條代碼:await asyncio.sleep(1),同樣會生成asyncio.sleep(1)協程對象,以及對應的任務task2,同時因await語法將控制權返回給事件循環;

    事件循環獲得控制權后,發現此時有三個任務task_main、task1、task2,由于task_main、task1都處于等待狀態,于是執行task3;

    task3在1秒后運行完成,返回控制權給事件循環;

    事件循環獲得控制權,發現此時有兩個任務task_main和task1,同時task_main在等待task1,于是會去執行task1任務;

    task1任務執行第2條代碼:print('hello'),執行完成后,任務也運行結束,將控制權返回給事件循環;

    事件循環獲得控制權后,發現此時有一個任務task_main,于是接著執行下一條代碼:await say_after(2, 'world'),繼續重復上述過程,直到這個協程任務結束;

    task_main執行最后一條代碼;

    事件循環關閉退出;

    代碼二的運行邏輯:

    asyncio.run(main()) 啟動一個事件循環,將入口點main()協程對象傳入,生成一個對應的任務task_main;

    事件循環運行任務task_main,然后執行前幾條代碼,創建兩個任務task1、task2,并注冊到事件循環中(此時事件循環一共有3個task),隨之執行程序直到await;

    第一個await:await task1,這里會阻塞當前任務task_main并將控制權返回給事件循環,事件循環獲取控制權,安排執行下一個任務task1;

    task1任務開始執行,直至遇到await asyncio.sleep(1),asyncio.sleep(1)協程對象開始異步執行,同時task1返回控制權給事件循環,事件循環獲取控制權后安排執行下一個任務task2;

    task2任務開始執行,直至遇到await asyncio.sleep(2),asyncio.sleep(2)協程對象開始異步執行,同時task2返回控制權給事件循環,事件循環獲取控制權后安排執行下一個任務;

    此時3個任務均處于await狀態,事件循環保持等待;

    1秒后asyncio.sleep(1)執行完成,task1取消阻塞,事件循環將安排task1執行,task1執行完成后返回控制權給事件循環,此時事件循環中一共兩個任務task_main、task2。

    此時task2任務處于await狀態,而task_main也取消了阻塞,事件循環安排task_main執行,執行一行代碼后遇到await task2,于是返回控制權給事件循環;

    此時2個任務均處于await狀態,事件循環保持等待;

    1秒后asyncio.sleep(2)執行完成,task2取消阻塞,事件循環將安排task2執行,task2執行完成后返回控制權給事件循環,此時事件循環中只剩任務task_main;

    于是事件循環安排task_main執行,task_main執行完成,asyncio.()函數收到信息也結束運行,整個程序結束

    運行的流程圖示

    (任務就緒后,就等待事件循環來調用了,此時需要await來阻塞主任務task_main,否則控制權一直在task_main手上,導致task_main任務執行完成,run()收到main()執行結束的消息后,事件循環也關閉并結束,程序也將退出)

    其實將第2個代碼中的await task1刪除,只保留await task2,結果中的輸出相同,并消耗相同的總時間。但只保留await task1的話,將沒有task2的輸出;
    如果將第2個代碼中的await task1和await task2都刪除,換成await asyncio.sleep(3),一樣會打印相同輸出,不過總時間會變為3秒;

    其中的原因需要理解協程的工作機制(事件循環和控制權)

    posted @ 2022-06-26 12:50  wuenwuen  閱讀(126)  評論(2編輯  收藏  舉報
    国产美女a做受大片观看