Python中的異步:async?和?await以及操作中的事件循環(huán)、回調(diào)和異常
引言
在現(xiàn)代編程中,異步操作是一個(gè)非常重要的概念,尤其是在處理 I/O 密集型任務(wù)時(shí)。使用異步操作可以顯著提高程序的性能和響應(yīng)速度。Python 提供了 async 和 await 關(guān)鍵字,使得編寫(xiě)異步代碼變得更加直觀和簡(jiǎn)潔。在這篇文章中,我們將深入探討 Python 的異步操作,并通過(guò)實(shí)際代碼示例來(lái)說(shuō)明其使用方法。
什么是異步操作?
異步操作是一種非阻塞的編程方式,它允許程序在等待某個(gè)操作(如 I/O 操作)完成的同時(shí)繼續(xù)執(zhí)行其他任務(wù)。與同步操作不同,異步操作不會(huì)阻塞主線程,而是通過(guò)回調(diào)、事件循環(huán)等機(jī)制來(lái)實(shí)現(xiàn)并發(fā)處理。
Python 中的異步編程基礎(chǔ)
async 和 await 關(guān)鍵字
async:定義一個(gè)異步函數(shù)。一個(gè)函數(shù)只需在def前面加上async關(guān)鍵字,就變成了異步函數(shù)。await:等待一個(gè)異步操作的完成。只能在異步函數(shù)中使用。
asyncio 模塊
asyncio 是 Python 的標(biāo)準(zhǔn)庫(kù)模塊,提供了對(duì)異步 I/O、事件循環(huán)、任務(wù)調(diào)度等功能的支持。
import asyncio
理論與代碼示例
定義異步函數(shù)
首先,我們來(lái)定義一個(gè)簡(jiǎn)單的異步函數(shù):
import asyncio
async def say_hello():
print("Hello")
await asyncio.sleep(1) # 模擬異步操作
print("World")
# 異步函數(shù)不會(huì)立即執(zhí)行,需要在事件循環(huán)中運(yùn)行
執(zhí)行異步函數(shù)
要運(yùn)行異步函數(shù),需要在事件循環(huán)中調(diào)用它們。asyncio.run 是一種簡(jiǎn)潔的方式來(lái)運(yùn)行異步函數(shù)。
async def main():
await say_hello()
# 使用 asyncio.run() 啟動(dòng)事件循環(huán)并執(zhí)行異步函數(shù)
if __name__ == "__main__":
asyncio.run(main())
異步 I/O 操作示例
讓我們編寫(xiě)一個(gè)更實(shí)際的示例,展示如何使用異步操作進(jìn)行 I/O 密集型任務(wù),如網(wǎng)絡(luò)請(qǐng)求。
import asyncio
import aiohttp # 需要安裝 aiohttp 庫(kù): pip install aiohttp
async def fetch_url(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
urls = [
"https://www.example.com",
"https://www.python.org",
"https://www.asyncio.org"
]
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks)
for url, content in zip(urls, results):
print(f"URL: {url}, Content Length: {len(content)}")
if __name__ == "__main__":
asyncio.run(main())
在這個(gè)示例中:
fetch_url:這是一個(gè)異步函數(shù),用于從指定的 URL 獲取內(nèi)容。main:在main函數(shù)中,我們定義了一組 URL,并為每個(gè) URL 創(chuàng)建一個(gè)異步任務(wù)。asyncio.gather:該函數(shù)并發(fā)地運(yùn)行所有任務(wù),并等待它們?nèi)客瓿伞?/li>aiohttp.ClientSession:這是一個(gè)異步 HTTP 客戶端會(huì)話,用于發(fā)送和接收 HTTP 請(qǐng)求。
高級(jí)用法:超時(shí)和取消任務(wù)
異步編程的一個(gè)重要優(yōu)勢(shì)是能夠設(shè)置超時(shí)和取消任務(wù)。我們可以使用 asyncio.wait_for 實(shí)現(xiàn)這一點(diǎn)。
import asyncio
async def long_running_task():
await asyncio.sleep(10)
return "Task completed"
async def main():
try:
result = await asyncio.wait_for(long_running_task(), timeout=5)
print(result)
except asyncio.TimeoutError:
print("The task took too long and was cancelled.")
if __name__ == "__main__":
asyncio.run(main())
在這個(gè)示例中,如果 long_running_task 在 5 秒內(nèi)沒(méi)有完成,則會(huì)拋出 asyncio.TimeoutError 異常。
異步編程的優(yōu)勢(shì)與局限性
優(yōu)勢(shì)
- 高效利用資源:異步編程可以在等待 I/O 操作完成時(shí)繼續(xù)執(zhí)行其他任務(wù),從而更高效地利用 CPU 資源。
- 提高響應(yīng)速度:對(duì)于 I/O 密集型任務(wù),異步操作可以顯著提高程序的響應(yīng)速度。
局限性
- 復(fù)雜性增加:異步編程相對(duì)于同步編程來(lái)說(shuō)更加復(fù)雜,需要處理事件循環(huán)、回調(diào)和異常等。
- 調(diào)試?yán)щy:異步代碼的調(diào)試和錯(cuò)誤追蹤相對(duì)較難。
事件循環(huán)、回調(diào)和異常
事件循環(huán)
理論解釋
事件循環(huán)是異步編程的核心,它不斷檢查和處理掛起的任務(wù)和 I/O 事件。Python 的 asyncio 模塊提供了對(duì)事件循環(huán)的支持。事件循環(huán)管理著所有異步任務(wù)的執(zhí)行,并在任務(wù)之間切換,從而實(shí)現(xiàn)并發(fā)。
具體代碼
import asyncio
async def say_hello():
print("Hello")
await asyncio.sleep(1)
print("World")
async def main():
# 獲取事件循環(huán)
loop = asyncio.get_event_loop()
# 創(chuàng)建任務(wù)
task = loop.create_task(say_hello())
# 運(yùn)行任務(wù)
await task
# 啟動(dòng)事件循環(huán)并執(zhí)行主函數(shù)
if __name__ == "__main__":
asyncio.run(main())
在這個(gè)示例中,asyncio.get_event_loop() 獲取了當(dāng)前的事件循環(huán),loop.create_task() 創(chuàng)建了一個(gè)任務(wù)并添加到事件循環(huán)中,await task 等待任務(wù)完成。
回調(diào)
理論解釋
回調(diào)函數(shù)是指在特定事件發(fā)生時(shí)自動(dòng)調(diào)用的函數(shù)。在異步編程中,回調(diào)函數(shù)通常用于處理異步任務(wù)的結(jié)果或異常。asyncio 提供了多種方式來(lái)設(shè)置回調(diào)函數(shù),包括 Future 和 Task 對(duì)象的 add_done_callback 方法。
具體代碼
import asyncio
async def slow_operation():
await asyncio.sleep(2)
return "Operation Completed"
def callback(future):
print(future.result())
async def main():
loop = asyncio.get_event_loop()
task = loop.create_task(slow_operation())
task.add_done_callback(callback)
await task
if __name__ == "__main__":
asyncio.run(main())
在這個(gè)示例中,我們定義了一個(gè)名為 callback 的回調(diào)函數(shù),用于處理 slow_operation 異步任務(wù)的結(jié)果。task.add_done_callback(callback) 將回調(diào)函數(shù)與任務(wù)關(guān)聯(lián),一旦任務(wù)完成,回調(diào)函數(shù)將被自動(dòng)調(diào)用并打印結(jié)果。
異常處理
理論解釋
在異步編程中,處理異常是至關(guān)重要的。任務(wù)在運(yùn)行過(guò)程中可能會(huì)拋出異常,我們需要捕獲和處理這些異常,以確保程序的穩(wěn)定性。asyncio 提供了多種方式來(lái)處理異步任務(wù)中的異常。
具體代碼
import asyncio
async def error_prone_operation():
await asyncio.sleep(1)
raise ValueError("An error occurred")
async def main():
try:
await error_prone_operation()
except ValueError as e:
print(f"Caught an exception: {e}")
if __name__ == "__main__":
asyncio.run(main())
在這個(gè)示例中,error_prone_operation 異步函數(shù)在執(zhí)行過(guò)程中可能會(huì)拋出 ValueError 異常。在 main 函數(shù)中,我們使用 try...except 塊來(lái)捕獲和處理這個(gè)異常,確保程序不會(huì)因?yàn)槲床东@的異常而崩潰。
異步任務(wù)中的異常處理
除了直接在異步函數(shù)中捕獲異常外,我們還可以在任務(wù)完成后檢查異常。asyncio.Task 對(duì)象的 exception 方法可以用于檢查任務(wù)是否拋出了異常。
import asyncio
async def error_prone_operation():
await asyncio.sleep(1)
raise ValueError("An error occurred")
async def main():
loop = asyncio.get_event_loop()
task = loop.create_task(error_prone_operation())
try:
await task
except ValueError as e:
print(f"Caught an exception: {e}")
# 或者在任務(wù)完成后檢查異常
if task.exception():
print(f"Task raised an exception: {task.exception()}")
if __name__ == "__main__":
asyncio.run(main())
在這個(gè)示例中,我們首先在 try...except 塊中捕獲異常,然后在任務(wù)完成后通過(guò) task.exception() 方法檢查任務(wù)是否拋出了異常。
超時(shí)處理
在某些情況下,異步操作可能需要設(shè)置超時(shí),以避免長(zhǎng)時(shí)間等待。asyncio.wait_for 函數(shù)可以用于設(shè)置異步操作的超時(shí)時(shí)間。
import asyncio
async def long_running_task():
await asyncio.sleep(10)
return "Task completed"
async def main():
try:
result = await asyncio.wait_for(long_running_task(), timeout=5)
print(result)
except asyncio.TimeoutError:
print("The task took too long and was cancelled.")
if __name__ == "__main__":
asyncio.run(main())
在這個(gè)示例中,如果 long_running_task 在 5 秒內(nèi)沒(méi)有完成,則會(huì)拋出 asyncio.TimeoutError 異常,我們可以捕獲并處理這個(gè)異常。
結(jié)論
異步編程是 Python 中處理并發(fā)和 I/O 密集型任務(wù)的一種強(qiáng)大工具。通過(guò)使用 async 和 await 關(guān)鍵字,以及 asyncio 模塊,我們可以編寫(xiě)出高效且響應(yīng)迅速的異步代碼。然而,異步編程也帶來(lái)了更高的復(fù)雜性,因此在使用時(shí)需要仔細(xì)權(quán)衡其優(yōu)勢(shì)和局限性。
通過(guò)了解事件循環(huán)、回調(diào)和異常處理,我們可以更好地掌握 Python 中的異步編程。事件循環(huán)是異步編程的核心,負(fù)責(zé)管理任務(wù)的調(diào)度和執(zhí)行;回調(diào)函數(shù)用于處理任務(wù)完成時(shí)的結(jié)果或異常;而異常處理則確保了程序的穩(wěn)定性和健壯性。
希望通過(guò)本文的詳細(xì)解釋和代碼示例,你能夠深入理解 Python 異步編程的底層原理和實(shí)際應(yīng)用。在實(shí)際項(xiàng)目中,合理使用這些機(jī)制,可以顯著提高程序的性能和響應(yīng)速度。
到此這篇關(guān)于Python中的異步:async 和 await以及操作中的事件循環(huán)、回調(diào)和異常的文章就介紹到這了,更多相關(guān)Python中的異步操作:async 和 await內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python構(gòu)建基礎(chǔ)的爬蟲(chóng)教學(xué)
在本篇內(nèi)容里小編給大家分享的是關(guān)于python構(gòu)建基礎(chǔ)的爬蟲(chóng)教學(xué)內(nèi)容,需要的朋友們學(xué)習(xí)下。2018-12-12
python os.path.isfile()因參數(shù)問(wèn)題判斷錯(cuò)誤的解決
今天小編就為大家分享一篇python os.path.isfile()因參數(shù)問(wèn)題判斷錯(cuò)誤的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11
Python代碼調(diào)試Debug的實(shí)用技巧分享
我們?nèi)粘?xiě)代碼過(guò)程中,難免會(huì)寫(xiě)出各類(lèi)錯(cuò)誤,這些錯(cuò)誤可能是語(yǔ)法錯(cuò)誤、邏輯錯(cuò)誤或運(yùn)行時(shí)錯(cuò)誤,所以本文為大家分享了一些Python調(diào)試Debug的技巧,感興趣的可以了解下2024-11-11
springboot配置文件抽離 git管理統(tǒng) 配置中心詳解
在本篇文章里小編給大家整理的是關(guān)于springboot配置文件抽離 git管理統(tǒng) 配置中心的相關(guān)知識(shí)點(diǎn)內(nèi)容,有需要的朋友們可以學(xué)習(xí)下。2019-09-09
python使用matplotlib定制繪圖的線型、標(biāo)記類(lèi)型
這篇文章主要給大家詳細(xì)介紹了python使用matplotlib定制繪圖的線型、標(biāo)記類(lèi)型,文中有詳細(xì)的代碼示例,具有一定的參考價(jià)值,需要的朋友可以參考下2023-07-07
利用python3 的pygame模塊實(shí)現(xiàn)塔防游戲
這篇文章主要介紹了利用python3 的pygame模塊實(shí)現(xiàn)塔防游戲,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12
Python中淺拷貝的四種實(shí)現(xiàn)方法小結(jié)
本文主要介紹了Python中淺拷貝的四種實(shí)現(xiàn)方法小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
PyTorch中torch.matmul()函數(shù)常見(jiàn)用法總結(jié)
torch.matmul()也是一種類(lèi)似于矩陣相乘操作的tensor連乘操作。但是它可以利用python中的廣播機(jī)制,處理一些維度不同的tensor結(jié)構(gòu)進(jìn)行相乘操作,這篇文章主要介紹了PyTorch中torch.matmul()函數(shù)用法總結(jié),需要的朋友可以參考下2023-04-04
基于Python實(shí)現(xiàn)音樂(lè)播放器的實(shí)現(xiàn)示例代碼
這篇文章主要介紹了如何利用Python編寫(xiě)簡(jiǎn)易的音樂(lè)播放器,文中的示例代碼講解詳細(xì),具有一的參考價(jià)值,需要的小伙伴可以參考一下2022-04-04

