並發程式設計是一種處理多個任務同時執行的程式設計方法。在Python中,asyncio是實現非同步程式設計的強大工具。基於協程的概念,asyncio 可以有效率地處理 I/O 密集型任務。本文將介紹asyncio的基本原理與使用方法
我們知道,在處理I/O操作時,使用多執行緒相比普通的單執行緒可以大大提高效率。那麼,為什麼我們還需要 asyncio?
多執行緒有很多優點,應用廣泛,但也有一定的限制:
正是為了解決這些問題,asyncio 應運而生。
我們先區分Sync(同步)和Async(非同步)的概念。
綜上所述,asyncio的工作原理是基於協程和事件循環的機制。 asyncio 透過使用協程進行非同步操作,並讓事件循環負責協程的調度和執行,實現了高效的非同步程式設計模型。
協程是 asyncio 中的重要概念。它們是輕量級執行單元,可以在任務之間快速切換,而無需執行緒切換的開銷。可以使用 async 關鍵字定義協程,await 關鍵字用於暫停協程的執行,並在某個操作完成後恢復。
這是一個簡單的範例程式碼,示範如何使用協程進行非同步程式設計:
import asyncio async def hello(): print("Hello") await asyncio.sleep(1) # Simulate a time-consuming operation print("World") # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(hello())
在此範例中,函數 hello() 是使用 async 關鍵字定義的協程。在協程內部,我們可以使用await來暫停其執行。這裡使用asyncio.sleep(1)來模擬一個耗時的操作。 run_until_complete() 方法將協程新增至事件循環並執行它。
asyncio主要用於處理I/O密集型任務,如網路請求、檔案讀寫等。它提供了一系列非同步I/O操作的API,可以與await關鍵字結合使用,輕鬆實現非同步程式設計。
這是一個簡單的範例程式碼,展示如何使用 asyncio 進行非同步網路請求:
import asyncio import aiohttp async def fetch(session, url): async with session.get(url) as response: return await response.text() async def main(): async with aiohttp.ClientSession() as session: html = await fetch(session, 'https://www.example.com') print(html) # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(main())
在此範例中,我們使用 aiohttp 函式庫來處理網路請求。函數 fetch() 是一個協程。它透過session.get()方法發起非同步GET請求,並使用await關鍵字等待回應傳回。函數 main() 是另一個協程。它在內部建立一個 ClientSession 物件以供複用,然後呼叫 fetch() 方法以取得網頁內容並列印。
注意:這裡我們使用aiohttp而不是requests函式庫,因為requests函式庫不相容asyncio,而aiohttp函式庫則相容。想要用好asyncio,尤其是發揮其強大的功能,很多時候需要對應的Python函式庫。
asyncio也提供了一些並發執行多個任務的機制,例如asyncio.gather()和asyncio.wait()。以下是一個範例程式碼,展示如何使用這些機制來並發執行多個協程任務:
import asyncio async def task1(): print("Task 1 started") await asyncio.sleep(1) print("Task 1 finished") async def task2(): print("Task 2 started") await asyncio.sleep(2) print("Task 2 finished") async def main(): await asyncio.gather(task1(), task2()) # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(main())
在這個範例中,我們定義了兩個協程任務task1()和task2(),它們都執行一些耗時的操作。協程 main() 透過 asyncio.gather() 同時啟動這兩個任務並等待它們完成。並發執行可以提高程式執行效率。
實際項目中,我們應該選擇多執行緒還是asyncio?有大佬總結的很形象:
import asyncio async def hello(): print("Hello") await asyncio.sleep(1) # Simulate a time-consuming operation print("World") # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(hello())
輸入一個列表。對於清單中的每個元素,我們要計算從 0 到該元素的所有整數的平方和。
import asyncio import aiohttp async def fetch(session, url): async with session.get(url) as response: return await response.text() async def main(): async with aiohttp.ClientSession() as session: html = await fetch(session, 'https://www.example.com') print(html) # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(main())
執行時間為計算耗時16.00943413000002秒
import asyncio async def task1(): print("Task 1 started") await asyncio.sleep(1) print("Task 1 finished") async def task2(): print("Task 2 started") await asyncio.sleep(2) print("Task 2 finished") async def main(): await asyncio.gather(task1(), task2()) # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(main())
執行時間為計算耗時 7.314132894999999 秒
在這段改進後的程式碼中,我們使用concurrent.futures.ProcessPoolExecutor建立進程池,然後使用executor.map()方法提交任務並取得結果。請注意,使用 executor.map() 後,如果需要取得結果,可以將結果迭代到清單中,或使用其他方法處理結果。
if io_bound: if io_slow: print('Use Asyncio') else: print('Use multi-threading') elif cpu_bound: print('Use multi-processing')
執行時間為計算耗時5.024221667秒
concurrent.futures.ProcessPoolExecutor和multiprocessing都是Python中用來實現多進程並發的函式庫。有些差異:
綜上所述,concurrent.futures.ProcessPoolExecutor是一個高層接口,封裝了底層多進程功能,適合簡單的多進程任務並行化。 multiprocessing是一個更底層的函式庫,提供了更多的控制和靈活性,適合需要對流程進行細粒度控制的場景。您需要根據具體需求選擇合適的庫。如果只是簡單的任務並行化,可以使用concurrent.futures.ProcessPoolExecutor來簡化程式碼;如果需要更多底層控制和通信,您可以使用多處理函式庫。
與多執行緒不同,asyncio 是單執行緒的,但其內部事件循環的機制允許它同時運行多個不同的任務,並且比多執行緒具有更強的自主控制能力。
asyncio中的任務在運行過程中不會被中斷,因此不會出現race condition的情況。
尤其是在I/O操作繁重的場景下,asyncio比多執行緒有更高的運作效率。因為asyncio中任務切換的成本遠小於執行緒切換的成本,而且asyncio可以啟動的任務數量遠大於多執行緒中的執行緒數量。
但要注意的是,很多情況下,使用asyncio需要特定的第三方函式庫的支持,例如上例中的aiohttp。而如果I/O操作又快又不重的話,使用多執行緒也能有效解決問題。
最後介紹一下部署Flask/FastAPI的理想平台:Leapcell。
Leapcell是專為現代分散式應用程式設計的雲端運算平台。其按需付費的定價模式確保沒有閒置成本,這意味著用戶只需為他們實際使用的資源付費。
在文件中了解更多!
Leapcell Twitter:https://x.com/LeapcellHQ
以上是高效能 Python:Asyncio的詳細內容。更多資訊請關注PHP中文網其他相關文章!