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