Python使用asyncio包實現(xiàn)異步編程方式
1. 異步編程
異步編程是一種編程范式,用于處理程序中需要等待異步操作完成后才能繼續(xù)執(zhí)行的情況。
異步編程允許程序在執(zhí)行耗時的操作時不被阻塞,而是在等待操作完成時繼續(xù)執(zhí)行其他任務。
這對于處理諸如文件 I/O、網(wǎng)絡請求、定時器等需要等待的操作非常有用。
使用異步編程通??梢詭硪韵潞锰帲?/p>
- 提高程序效率和性能:異步編程使得程序在執(zhí)行耗時的 I/O 操作(如網(wǎng)絡請求、文件讀寫、數(shù)據(jù)庫查詢等)時不會被阻塞,減少了等待時間,充分利用了系統(tǒng)資源。
- 改善用戶體驗:在 Web 開發(fā)中,異步編程可以確保服務器在處理大量并發(fā)請求時能夠快速地響應用戶,從而提高了 Web 應用的響應速度和用戶體驗。
2 async/await和asyncio包
2.1 異步函數(shù)的定義
在Python中實現(xiàn)異步函數(shù)的定義需要兩個關鍵字(async
和await
)。
async
:async
關鍵字聲明一個異步函數(shù)。它可以在執(zhí)行過程中暫停并允許其他代碼執(zhí)行。當你調(diào)用一個異步函數(shù)時,它會立即返回一個協(xié)程對象而不是實際的結果。異步函數(shù)適用于執(zhí)行耗時的I/O操作,例如網(wǎng)絡請求、文件讀寫、數(shù)據(jù)庫查詢等。這些操作通常涉及到等待外部資源的響應或者數(shù)據(jù)的傳輸,而在等待的過程中,CPU可以執(zhí)行其他任務,從而提高程序的效率。await
:await
關鍵字在Python中用于等待一個異步操作完成。當調(diào)用異步函數(shù)時,使用await
關鍵字可以暫時掛起當前的異步函數(shù)的執(zhí)行,將CPU控制權還給事件循環(huán)(Event Loop)。接著事件循環(huán)可以將執(zhí)行權轉(zhuǎn)移到其他任務上,而不是一直等待當前的異步函數(shù)完成。當被await
的異步操作完成后,事件循環(huán)會通知原來的異步函數(shù),使得它可以繼續(xù)執(zhí)行后續(xù)的操作。
在Python中異步函數(shù)的定義需要同時滿足以下兩個條件:
- 使用
async def
關鍵字聲明函數(shù)。 - 函數(shù)內(nèi)部包含異步操作,并且使用了
await
關鍵字等待異步操作完成。如果一個函數(shù)中只使用了async def
聲明,但其中任何異步操作,也沒有使用await
關鍵字,那么它實際上就是一個普通的同步函數(shù),而不是一個異步函數(shù)。
2.2 事件循環(huán)
事件循環(huán)(Event Loop)是異步編程中負責管理和調(diào)度異步任務執(zhí)行的機制。
事件循環(huán)的工作原理類似于一個持續(xù)運行的循環(huán),它在每一輪循環(huán)中都會執(zhí)行以下幾個步驟:
- 等待任務就緒: 事件循環(huán)會等待所有注冊的異步任務就緒,包括等待 I/O 操作完成、等待計時器超時等。
- 選擇就緒任務:一旦有任務就緒,事件循環(huán)會選擇其中一個任務進行執(zhí)行。
- 執(zhí)行任務:事件循環(huán)會執(zhí)行所選擇的就緒任務,直到任務完成或者遇到
await
關鍵字,需要暫時掛起任務的執(zhí)行。 - 掛起任務:如果任務遇到
await
關鍵字,它會將控制權交還給事件循環(huán),并等待await
后面的異步操作完成。 - 繼續(xù)執(zhí)行其他任務:在等待
await
的異步操作完成的過程中,事件循環(huán)會繼續(xù)執(zhí)行其他就緒的任務,從而實現(xiàn)了并發(fā)執(zhí)行的效果。 - 異步操作完成: 當一個 await 后面的異步操作完成后,事件循環(huán)會通知原來的任務,使得它可以繼續(xù)執(zhí)行后續(xù)的操作。
2.2 asyncio包
asyncio
包python中常用的異步編程框架,這里使用該框架完成一個簡單的異步編程案例,具體如下:
import time import datetime import asyncio async def async_read_file(): print("async讀文件開始:",datetime.datetime.fromtimestamp(time.time())) await asyncio.sleep(20) print("async讀文件完成:",datetime.datetime.fromtimestamp(time.time())) def computer(): print("普通計算密集型任務:",datetime.datetime.fromtimestamp(time.time())) sum=0 for i in range(1000000): if i%250000==0 and i!=0: print("普通計算密集型任務正在執(zhí)行:",datetime.datetime.fromtimestamp(time.time())) for j in range(500): sum+=i+j-2*j print("普通計算密集型任務完成:",datetime.datetime.fromtimestamp(time.time())) def computer2(): print("普通CPU密集型任務:",datetime.datetime.fromtimestamp(time.time())) sum=0 for i in range(1000000): if i%250000==0 and i!=0: print("普通CPU密集型任務正在執(zhí)行:",datetime.datetime.fromtimestamp(time.time())) for j in range(5000): sum+=i+j-2*j print("普通CPU密集型任務完成:",datetime.datetime.fromtimestamp(time.time())) async def asy_main(): task=loop.create_task(async_read_file()) # 創(chuàng)建一個任務,并添加到事件循環(huán),等待執(zhí)行 task2=loop.run_in_executor(None,computer)# 將普通函數(shù)read_file添加到事件循環(huán)中,等待執(zhí)行 task3=loop.run_in_executor(None,computer2)# 將普通函數(shù)read_file2添加到事件循環(huán)中,等待執(zhí)行 await task3 await task2 await task loop=asyncio.get_event_loop() # 創(chuàng)建一個事件循環(huán) loop.run_until_complete(asy_main())
其執(zhí)行結果如下:
普通計算密集型任務: 2024-05-15 18:29:19.702689
普通CPU密集型任務: 2024-05-15 18:29:19.708280
async讀文件開始: 2024-05-15 18:29:19.738654
普通計算密集型任務正在執(zhí)行: 2024-05-15 18:29:21.441072
普通計算密集型任務正在執(zhí)行: 2024-05-15 18:29:23.192585
普通計算密集型任務正在執(zhí)行: 2024-05-15 18:29:24.936979
普通計算密集型任務完成: 2024-05-15 18:29:26.712930
普通CPU密集型任務正在執(zhí)行: 2024-05-15 18:29:32.539679
async讀文件完成: 2024-05-15 18:29:39.752731
普通CPU密集型任務正在執(zhí)行: 2024-05-15 18:29:41.813872
普通CPU密集型任務正在執(zhí)行: 2024-05-15 18:29:51.103737
普通CPU密集型任務完成: 2024-05-15 18:30:00.433402
從代碼運行結果中可以看到,兩個計算密集型的任務task2、task3和異步函數(shù)task添加到事件循環(huán)上之后,在等待異步操作task完成的過程中,CPU并沒有閑著,而是在執(zhí)行task2和task3的任務。
Tips:雖然當下的執(zhí)行結果中寫完成了computer()的計算,后完成了computer2()的計算,但多次執(zhí)行上述程序的時候也出現(xiàn)了兩個函數(shù)交替執(zhí)行的結果。
為了與上述代碼形成對比,執(zhí)行下述代碼:
import asyncio import datetime async def async_task(name, delay): print(f"Task {name} started:",datetime.datetime.now()) await asyncio.sleep(delay) print(f"Task {name} finished:",datetime.datetime.now()) async def main(): await async_task("A", 2) await async_task("B", 1) await async_task("C", 3) asyncio.run(main())
其代碼執(zhí)行結果如下:
Task A started: 2024-05-21 17:45:24.324535
Task A finished: 2024-05-21 17:45:26.326109
Task B started: 2024-05-21 17:45:26.326250
Task B finished: 2024-05-21 17:45:27.327795
Task C started: 2024-05-21 17:45:27.327923
Task C finished: 2024-05-21 17:45:30.329475
從執(zhí)行結果上可以看到這三個異步操作是順序執(zhí)行的,并沒有同時執(zhí)行。
這是因為在執(zhí)行await
后面的異步操作時事件循環(huán)中只有一個任務。
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- Python使用asyncio實現(xiàn)異步操作的示例
- Python中asyncio的多種用法舉例(異步同步)
- Python使用asyncio處理異步編程的代碼示例
- Python異步庫asyncio、aiohttp詳解
- python協(xié)程異步IO中asyncio的使用
- Python使用asyncio標準庫對異步IO的支持
- Python協(xié)程異步爬取數(shù)據(jù)(asyncio+aiohttp)實例
- Python使用asyncio異步時的常見問題總結
- Python asyncio異步編程常見問題小結
- Python asyncio異步編程簡單實現(xiàn)示例
- Python中asyncio庫實現(xiàn)異步編程的示例
相關文章
Python保留指定位數(shù)小數(shù)的5種方法總結
很多小伙伴在學習python的時候可能會遇到對數(shù)據(jù)進行格式化輸出的需求,其中最常見的需求為保留幾位小數(shù),這篇文章主要給大家介紹了關于Python保留指定位數(shù)小數(shù)的5種方法,需要的朋友可以參考下2023-08-08python tkinter GUI繪制,以及點擊更新顯示圖片代碼
這篇文章主要介紹了python tkinter GUI繪制,以及點擊更新顯示圖片代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03卸載tensorflow-cpu重裝tensorflow-gpu操作
這篇文章主要介紹了卸載tensorflow-cpu重裝tensorflow-gpu操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06Python+matplotlib實現(xiàn)堆疊圖的繪制
Matplotlib作為Python的2D繪圖庫,它以各種硬拷貝格式和跨平臺的交互式環(huán)境生成出版質(zhì)量級別的圖形。本文將利用Matplotlib庫繪制堆疊圖,感興趣的可以了解一下2022-03-03