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é)程對象而不是實際的結(jié)果。異步函數(shù)適用于執(zhí)行耗時的I/O操作,例如網(wǎng)絡請求、文件讀寫、數(shù)據(jù)庫查詢等。這些操作通常涉及到等待外部資源的響應或者數(shù)據(jù)的傳輸,而在等待的過程中,CPU可以執(zhí)行其他任務,從而提高程序的效率。await:await關鍵字在Python中用于等待一個異步操作完成。當調(diào)用異步函數(shù)時,使用await關鍵字可以暫時掛起當前的異步函數(shù)的執(zhí)行,將CPU控制權(quán)還給事件循環(huán)(Event Loop)。接著事件循環(huán)可以將執(zhí)行權(quán)轉(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關鍵字,它會將控制權(quán)交還給事件循環(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í)行結(jié)果如下:
普通計算密集型任務: 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
從代碼運行結(jié)果中可以看到,兩個計算密集型的任務task2、task3和異步函數(shù)task添加到事件循環(huán)上之后,在等待異步操作task完成的過程中,CPU并沒有閑著,而是在執(zhí)行task2和task3的任務。
Tips:雖然當下的執(zhí)行結(jié)果中寫完成了computer()的計算,后完成了computer2()的計算,但多次執(zhí)行上述程序的時候也出現(xiàn)了兩個函數(shù)交替執(zhí)行的結(jié)果。
為了與上述代碼形成對比,執(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í)行結(jié)果如下:
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í)行結(jié)果上可以看到這三個異步操作是順序執(zhí)行的,并沒有同時執(zhí)行。
這是因為在執(zhí)行await后面的異步操作時事件循環(huán)中只有一個任務。
總結(jié)
以上為個人經(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異步時的常見問題總結(jié)
- Python asyncio異步編程常見問題小結(jié)
- Python asyncio異步編程簡單實現(xiàn)示例
- Python中asyncio庫實現(xiàn)異步編程的示例
相關文章
Python保留指定位數(shù)小數(shù)的5種方法總結(jié)
很多小伙伴在學習python的時候可能會遇到對數(shù)據(jù)進行格式化輸出的需求,其中最常見的需求為保留幾位小數(shù),這篇文章主要給大家介紹了關于Python保留指定位數(shù)小數(shù)的5種方法,需要的朋友可以參考下2023-08-08
python tkinter GUI繪制,以及點擊更新顯示圖片代碼
這篇文章主要介紹了python tkinter GUI繪制,以及點擊更新顯示圖片代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03
卸載tensorflow-cpu重裝tensorflow-gpu操作
這篇文章主要介紹了卸載tensorflow-cpu重裝tensorflow-gpu操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06
Python+matplotlib實現(xiàn)堆疊圖的繪制
Matplotlib作為Python的2D繪圖庫,它以各種硬拷貝格式和跨平臺的交互式環(huán)境生成出版質(zhì)量級別的圖形。本文將利用Matplotlib庫繪制堆疊圖,感興趣的可以了解一下2022-03-03

