Python中asyncio的多種用法舉例(異步同步)
1 引言
Python 的 asyncio
模塊為異步編程提供了強(qiáng)大的支持,但在某些場景下,我們可能需要處理異步任務(wù)與非異步(同步)任務(wù)的順序執(zhí)行或并行執(zhí)行。本篇文章將逐步帶你了解如何在 Python 中處理這些不同類型的任務(wù)。
2 順序執(zhí)行非異步任務(wù)
在日常編程中,最常見的情況之一就是順序執(zhí)行一系列非異步(同步)的任務(wù)。這些任務(wù)在同一個(gè)線程中執(zhí)行,通常會(huì)阻塞主程序的運(yùn)行,直到任務(wù)完成。
示例代碼:
import time def blocking_task(id): print(f"Blocking task {id} started") time.sleep(2) # 模擬一個(gè)阻塞操作 print(f"Blocking task {id} finished") # 順序執(zhí)行多個(gè)同步任務(wù) def main(): for i in range(3): blocking_task(i) main()
解釋:
在此代碼中,每個(gè)任務(wù)按順序執(zhí)行,
time.sleep()
會(huì)阻塞當(dāng)前線程,直到所有任務(wù)結(jié)束。這種順序執(zhí)行的方式雖然簡單直接,但效率較低,尤其當(dāng)任務(wù)涉及 I/O 操作時(shí),會(huì)浪費(fèi)大量時(shí)間。
3 順序執(zhí)行異步任務(wù)
如果我們希望提高任務(wù)的執(zhí)行效率,可以考慮使用異步任務(wù)。異步任務(wù)不會(huì)阻塞主線程,而是會(huì)等待特定的事件(例如 I/O 操作的完成),然后繼續(xù)執(zhí)行。
示例代碼:
import asyncio async def async_task(id): print(f"Async task {id} started") await asyncio.sleep(2) # 模擬異步操作 print(f"Async task {id} finished") # 順序執(zhí)行多個(gè)異步任務(wù) async def main(): for i in range(3): await async_task(i) asyncio.run(main())
解釋:
通過使用 async def
定義異步函數(shù),await
關(guān)鍵字用于暫停任務(wù)的執(zhí)行并等待異步操作完成。雖然這些任務(wù)是異步的,但由于我們使用了 await
,它們?nèi)匀皇琼樞驁?zhí)行的。
3 并行執(zhí)行異步任務(wù)
在某些情況下,我們可能希望異步任務(wù)能夠并行執(zhí)行,而不是一個(gè)接一個(gè)地等待。此時(shí)可以使用
asyncio.gather()
,它允許我們并行運(yùn)行多個(gè)異步任務(wù),從而提高程序效率。
示意圖:
示例代碼:
import asyncio async def async_task(id): print(f"Async task {id} started") await asyncio.sleep(2) print(f"Async task {id} finished") # 并行執(zhí)行多個(gè)異步任務(wù) async def main(): tasks = [async_task(i) for i in range(3)] await asyncio.gather(*tasks) asyncio.run(main())
解釋:
asyncio.gather()
會(huì)并行執(zhí)行多個(gè)異步任務(wù),而不是按順序等待。任務(wù)在后臺(tái)同時(shí)運(yùn)行,極大提高了效率,尤其是當(dāng)任務(wù)需要等待 I/O 時(shí)(例如網(wǎng)絡(luò)請求、文件操作等)。
? 注意:
tasks = [async_task(i) for i in range(3)]
這個(gè)時(shí)候不會(huì)執(zhí)行async_task函數(shù),只是創(chuàng)建協(xié)程對象,還沒真正的啟動(dòng)。
- 當(dāng)你調(diào)用 async_task(i) 時(shí),它不會(huì)立即執(zhí)行,而是返回一個(gè) 協(xié)程對象(coroutine object),這個(gè)對象代表一個(gè)等待執(zhí)行的異步任務(wù)。
- 只有當(dāng)你 await 這個(gè)協(xié)程對象或者將它傳遞給
asyncio.gather()、asyncio.create_task()、asyncio.run()
等函數(shù)時(shí),協(xié)程才會(huì)開始執(zhí)行。
4 并行執(zhí)行非異步任務(wù)(阻塞任務(wù))
如果你有一些外部庫提供的阻塞任務(wù)(如文件讀寫、網(wǎng)絡(luò)操作等),這些任務(wù)無法直接變?yōu)楫惒胶瘮?shù)。為了與異步任務(wù)并行執(zhí)行這些阻塞任務(wù),asyncio.run_in_executor()
是你的好幫手。
示意圖:
示例代碼:使用線程池并行執(zhí)行同步任務(wù)
import asyncio import time from concurrent.futures import ThreadPoolExecutor def blocking_task(id): print(f"Blocking task {id} started") time.sleep(2) print(f"Blocking task {id} finished") async def main(): with ThreadPoolExecutor() as pool: tasks = [ asyncio.get_event_loop().run_in_executor(pool, blocking_task, i) for i in range(3) ] await asyncio.gather(*tasks) asyncio.run(main())
解釋:
run_in_executor()
將阻塞的任務(wù)交給線程池(或進(jìn)程池)執(zhí)行,而不會(huì)阻塞主事件循環(huán)。這使得我們可以同時(shí)處理異步任務(wù)和阻塞任務(wù)。
使用進(jìn)程池還是線程池?
- 線程池(ThreadPoolExecutor):適用于 I/O 密集型任務(wù),如文件操作或網(wǎng)絡(luò)請求。這類任務(wù)通常會(huì)等待外部事件完成,因此不需要消耗大量 CPU 資源。
- 進(jìn)程池(ProcessPoolExecutor):適合 CPU 密集型任務(wù),如數(shù)據(jù)處理和計(jì)算。使用進(jìn)程池可以充分利用多核 CPU,提升性能。
示例代碼:使用進(jìn)程池
from concurrent.futures import ProcessPoolExecutor import asyncio def cpu_intensive_task(id): print(f"CPU task {id} started") result = sum(i*i for i in range(10**6)) # 模擬CPU密集任務(wù) print(f"CPU task {id} finished with result: {result}") async def main(): with ProcessPoolExecutor() as pool: tasks = [ asyncio.get_event_loop().run_in_executor(pool, cpu_intensive_task, i) for i in range(3) ] await asyncio.gather(*tasks) asyncio.run(main())
5 總結(jié)
- 順序執(zhí)行非異步任務(wù):通常用于簡單的任務(wù),但效率低下,容易阻塞線程。
- 順序執(zhí)行異步任務(wù):使用
asyncio
提供的異步函數(shù),能夠在等待 I/O 時(shí)不阻塞主線程。 - 并行執(zhí)行異步任務(wù):通過
asyncio.gather()
,可以輕松并行多個(gè)異步任務(wù),極大提高執(zhí)行效率。 - 并行執(zhí)行非異步任務(wù):通過
run_in_executor()
將阻塞任務(wù)交給線程池或進(jìn)程池,保證異步任務(wù)和同步任務(wù)可以并行執(zhí)行。 - 線程池 vs 進(jìn)程池:選擇線程池處理 I/O 密集型任務(wù),進(jìn)程池處理 CPU 密集型任務(wù)。
通過這些技巧,你可以在 Python 中輕松管理各種類型的任務(wù),實(shí)現(xiàn)高效并行處理。
到此這篇關(guān)于Python中asyncio的多種用法的文章就介紹到這了,更多相關(guān)Python中asyncio多種用法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Python使用asyncio實(shí)現(xiàn)異步操作的示例
- Python使用asyncio處理異步編程的代碼示例
- Python使用asyncio包實(shí)現(xiàn)異步編程方式
- Python異步庫asyncio、aiohttp詳解
- python協(xié)程異步IO中asyncio的使用
- Python使用asyncio標(biāo)準(zhǔn)庫對異步IO的支持
- Python協(xié)程異步爬取數(shù)據(jù)(asyncio+aiohttp)實(shí)例
- Python使用asyncio異步時(shí)的常見問題總結(jié)
- Python asyncio異步編程常見問題小結(jié)
- Python asyncio異步編程簡單實(shí)現(xiàn)示例
- Python中asyncio庫實(shí)現(xiàn)異步編程的示例
相關(guān)文章
python爬蟲之利用Selenium+Requests爬取拉勾網(wǎng)
這篇文章主要介紹了python爬蟲之利用Selenium+Requests爬取拉勾網(wǎng),文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)python爬蟲的小伙伴們有很好的幫助,需要的朋友可以參考下2021-04-04python:pandas合并csv文件的方法(圖書數(shù)據(jù)集成)
下面小編就為大家分享一篇python:pandas合并csv文件的方法(圖書數(shù)據(jù)集成),具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-04-04Scrapy啟動(dòng)報(bào)錯(cuò)invalid syntax的解決
這篇文章主要介紹了Scrapy啟動(dòng)報(bào)錯(cuò)invalid syntax的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09Python使用Crypto庫實(shí)現(xiàn)加密解密的示例詳解
這篇文章主要為大家詳細(xì)介紹了Python如何使用Crypto庫實(shí)現(xiàn)加密解密的功能,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Python有一定的幫助,需要的可以參考一下2023-01-01django執(zhí)行原生SQL查詢的實(shí)現(xiàn)
本文主要介紹了django執(zhí)行原生SQL查詢的實(shí)現(xiàn),主要有兩種方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08