Python使用asyncio包實(shí)現(xiàn)異步編程方式
1. 異步編程
異步編程是一種編程范式,用于處理程序中需要等待異步操作完成后才能繼續(xù)執(zhí)行的情況。
異步編程允許程序在執(zhí)行耗時(shí)的操作時(shí)不被阻塞,而是在等待操作完成時(shí)繼續(xù)執(zhí)行其他任務(wù)。
這對(duì)于處理諸如文件 I/O、網(wǎng)絡(luò)請(qǐng)求、定時(shí)器等需要等待的操作非常有用。

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

