Python協程(二) Asyncio入門


Asyncio模塊提供了使用協程構建並發應用的工具。它使用一種單線程的方式實現並發,一般會在程序阻塞I/O操作的時候發生上下文切換,如讀寫文件,或者請求網絡。

同時Asyncio也支持調度代碼在將來的某個特定事件運行,從而支持一個協程等待另一個協程完成,以處理系統信號和識別其他一些事件。

基本概念

Asyncio里面主要以下幾個需要關注的基本概念。

1、Eventloop(事件循環)

事件循環是每個 Asyncio 應用的核心。 事件循環會運行異步任務和回調,執行網絡 IO 操作,以及運行子進程。

程序開啟一個無限循環,並且把一些協程函數注冊到這個事件循環上,事件循環會循環執行這些函數 (但同時只能執行一個),當執行到某個函數時,如果它正在等待 I/O 返回,事件循環會暫停它的執行去執行其他的函數;當某個函數完成 I/O 后會恢復,下次循環到它的時候繼續執行。因此,這些異步函數可以協同 (Cooperative) 運行:這就是事件循環的目標。

2、可等待對象

如果一個對象可以在 await 語句中使用,那么它就是 可等待 對象。許多 Asyncio API 都被設計為接受可等待對象。

可等待 對象有三種主要類型: 協程Future 和 任務.

3、協程 (Coroutine)

協程 (Coroutine) 本質上是一個函數,特點是在代碼塊中可以將執行權交給其他協程。

它的調用不會立即執行函數,而是會返回一個協程對象。協程對象需要注冊到事件循環中,由事件循環調用。

協程通過 async/await 語法進行聲明,是編寫 Asyncio 應用的推薦方式。 例如,以下代碼段(需要 Python 3.7+)會打印 "hello",等待 1 秒,再打印 "world":

>>> import asyncio >>> async def main(): ... print('hello') ... await asyncio.sleep(1) ... print('world') >>> asyncio.run(main()) hello world

注意:簡單地調用一個協程並不會將其加入執行日程。

要真正運行一個協程,asyncio 提供了三種主要機制:

  • asyncio.run() 函數用來運行最高層級的入口點 "main()" 函數 (參見上面的示例。)

  • 等待一個協程。以下代碼段會在等待 1 秒后打印 "hello",然后 再次 等待 2 秒后打印 "world":

  • 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())
    
    # 運行結果
    started at 10:38:33
    hello
    world
    finished at 10:38:36
  • asyncio.create_task() 函數用來並發運行作為 asyncio 任務 的多個協程。我們修改以上示例,並發 運行兩個 say_after 協程:
  • 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')}")
    
        # Wait until both tasks are completed (should take
        # around 2 seconds.)
        await task1
        await task2
    
        print(f"finished at {time.strftime('%X')}")
    
    # 運行結果(注意,輸出顯示代碼段的運行時間比之前快了 1 秒:)
    started at 10:40:58
    hello
    world
    finished at 10:41:00

4、Future

Future 是一種特殊的 低層級 可等待對象,表示一個異步操作的 最終結果

當一個 Future 對象 被等待,這意味着協程將保持等待直到該 Future 對象在其他地方操作完畢或取消。

Future 對象通常用來鏈接 底層回調式代碼 和高層異步/等待式代碼,在 Asyncio 中需要 Future 對象以便允許通過 async/await 使用基於回調的代碼。

事件循環可以監視Future對象是否完成。從而允許應用的一部分等待另一部分完成一些工作。

異步操作結束后會把最終結果設置到這個 Future 對象上。Future 是對協程的封裝,通常情況下 沒有必要 在應用層級的代碼中創建 Future 對象。

Future 對象有時會由庫和某些 asyncio API 暴露給用戶,用作可等待對象:

async def main():
    await function_that_returns_a_future_object()

    # this is also valid:
    await asyncio.gather(
        function_that_returns_a_future_object(),
        some_python_coroutine()
    )

5、Task

Task是Future的一個子類,它用來包裝和管理一個協程的執行。任務所需的資源可用時,事件循環會調度任務允許,並生成一個結果,從而可以由其他協程消費。

Task 對象被用來在事件循環中運行協程。如果一個協程在等待一個 Future 對象,Task 對象會掛起該協程的執行並等待該 Future 對象完成。當該 Future 對象 完成,被打包的協程將恢復執行。

事件循環使用協同日程調度: 一個事件循環每次運行一個 Task 對象。而一個 Task 對象會等待一個 Future 對象完成,該事件循環會運行其他 Task、回調或執行 IO 操作。

使用高層級的 asyncio.create_task() 函數來創建 Task 對象,也可用低層級的 loop.create_task() 或 ensure_future() 函數。不建議手動實例化 Task 對象。

要取消一個正在運行的 Task 對象可使用 cancel() 方法。調用此方法將使該 Task 對象拋出一個 CancelledError 異常給打包的協程。如果取消期間一個協程正在等待一個 Future 對象,該 Future 對象也將被取消。

 

參考文章:

https://zhuanlan.zhihu.com/p/69210021
https://segmentfault.com/q/1010000007863343
https://www.jianshu.com/p/2afbe455b526
https://blog.csdn.net/weixin_45139605/article/details/90798253
https://blog.csdn.net/weixin_41599977/article/details/93656042
https://docs.python.org/zh-cn/3/library/asyncio-eventloop.html#creating-futures-and-tasks
https://python-parallel-programmning-cookbook.readthedocs.io/zh_CN/latest/chapter4/03_Event_loop_management_with_Asyncio.html
https://learnku.com/docs/pymotw/asyncio-asynchronous-io-event-loop-and-concurrency-tools/3423
https://docs.python.org/zh-cn/3/library/asyncio-task.html
http://www.manongjc.com/article/75292.html
https://segmentfault.com/a/1190000012631063
https://www.dongwm.com/post/142/
https://www.jianshu.com/p/71b90a578668
https://realpython.com/async-io-python/#the-event-loop-and-asynciorun

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM