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