在程式設計世界中,「非阻塞」的概念無所不在。 JavaScript 開發人員經常使用術語“非同步”,因為它是 JavaScript 的優點之一。然而,要真正理解非同步編程,必須掌握並發和平行編程的概念。
當多個獨立實體同時工作時,程式設計是並發的。這並不一定意味著這些任務在完全相同的時間運行。相反,它意味著任務透過共享資源(例如 CPU 時間)隨著時間的推移而不斷取得進展。並發程式設計的主要優點是它的穩健性:如果一個進程崩潰,程式的其餘部分繼續運行。
如果一個演算法可以將其工作分成幾個部分,那麼它就是並行的。擁有的處理器越多,您從並行性中受益就越多。高效的並行編程可優化現代機器的資源以獲得更好的性能。
並發範例:
想像一下您正在準備一頓飯,需要烤一些肉並製作醬汁。首先將肉放在烤架上。當肉烤的時候,你可以把番茄和其他蔬菜切碎當作醬汁。然後,你開始煮醬汁,同時偶爾檢查一下肉。在這裡,兩項任務(烤肉和製作醬汁)都在進行中,但您正在它們之間切換注意力。這代表並發。
平行範例:
現在,假設您有一位朋友可以幫助您。當您專注於烤肉時,您的朋友則負責製作醬汁。這兩項任務同時完成,無需在它們之間切換注意力。這代表並行性。
非同步程式設計涉及處理程序外部發生的輸入/輸出 (I/O) 操作,例如使用者輸入、列印到終端、從套接字讀取或寫入磁碟。非同步 I/O 的主要特徵是:
操作所花費的時間與CPU無關。相反,它取決於磁碟速度、網路延遲和其他外部條件等因素。
程式無法預測操作何時結束。
對於具有大量 I/O 的服務(如 Web 伺服器、資料庫和部署腳本),最佳化這些操作可以大大提高效能。
讓我們看看阻塞程式碼和非阻塞程式碼的範例。
考慮一個簡單的程序:
import time def task(): time.sleep(2) print("Hello") for _ in range(3): task()
在這個同步程式中,每個任務都會等待前一個任務完成,導致延遲。
現在,讓我們來看看使用 asyncio 的非同步版本:
import asyncio async def task(): await asyncio.sleep(2) print("Hello") async def main(): tasks = [task() for _ in range(3)] await asyncio.gather(*tasks) asyncio.run(main())
在這個非同步程式中,任務同時運行,減少了總執行時間。讓我們探索非同步程式設計的元件。
事件循環、協程和 future 是非同步 Python 程式的基本元素。
事件循環:管理任務切換和執行流程,追蹤要非同步運行的任務。
協程:可以暫停和恢復的特殊功能,允許在等待期間運行其他任務。協程指定任務切換事件應在函數中發生的位置,將控制權傳回事件循環。協程通常由事件循環建立並內部儲存在任務佇列中。
Futures: 協程結果的佔位符,儲存結果或異常。一旦事件循環啟動一個協程,就會建立一個對應的 future 來儲存協程的結果,如果在協程執行期間拋出異常,則會建立一個異常。
解釋完 Python 非同步程式設計的關鍵部分後,讓我們來寫一些程式碼。
現在您已經了解了非同步程式設計模式,讓我們編寫一個小腳本並分析執行情況。這是一個簡單的非同步腳本:
import asyncio async def task(): await asyncio.sleep(2) print("Hello") async def main(): tasks = [task() for _ in range(3)] await asyncio.gather(*tasks) asyncio.run(main())
在上面的程式碼中,即使另一個正在執行的任務正在睡眠(阻塞),我們也嘗試繼續執行其他任務。注意任務和主函數前面的 async 關鍵字。
這些函數現在是協程。
Coroutines functions in Python are preceded by the keyword async. The main() function here is the task coordinator or our single event loop, as it executes all tasks using the async.gather method. The asyncio.gather function runs awaitable objects concurrently.
Output:
Hello Hello Hello Program executed in 2.01 seconds.
When each task reaches await asyncio.sleep(2), it simply goes to the next task and comes back when it's finished. It's like saying, "I am going to sleep for 2 seconds. Do something else."
Let's see the synchronous version for a quick comparison.
import time def task(): time.sleep(2) print("Hello") for _ in range(3): task()
In the code above, we are going the traditional programming way in Python. You will notice that the execution of the process will take much more time.
Output:
Hello Hello Hello Program executed in 6.01 seconds.
Now you can notice the execution time. Think of time.sleep() as a blocking task and asyncio.sleep() as a non-blocking or long task. In asynchronous programming, the benefit of awaiting something, like asyncio.sleep(), is that the surrounding function can temporarily cede control to another function that is ready to execute immediately.
With some basic examples of asynchronous programming in Python understood, let's explore the rules of asynchronous programming in Python.
Coroutines: Coroutines cannot be executed directly. If you try to run a coroutine function directly, it returns a coroutine object. Instead, use asyncio.run():
import asyncio async def hello(): await asyncio.sleep(1) print('Hello') asyncio.run(hello())
Awaitable Objects: Coroutines, futures, and tasks are the main awaitable objects. Python coroutines are awaitables and can be awaited from other coroutines.
Await Keyword:await can only be used within async functions.
async def hello(): await asyncio.sleep(1) print("Hello")
Compatibility: Not all Python modules are compatible with asynchronous programming. For example, replacing await asyncio.sleep() with time.sleep() will cause an error. You can check the list of compatible and maintained modules here.
In the next section, we will explore a common use of asynchronous programming, HTTP requests.
Let's take a look at the following piece of code:
import aiohttp import asyncio async def fetch(session, city): url = f"https://www.prevision-meteo.ch/services/json/{city}" async with session.get(url) as response: data = await response.json() print(f"Temperature at {city}: {data['current_condition']['tmp']} C") async def main(): async with aiohttp.ClientSession() as session: cities = ['paris', 'toulouse', 'marseille'] tasks = [fetch(session, city) for city in cities] await asyncio.gather(*tasks) asyncio.run(main())
In the code above, we create two asynchronous functions: one to fetch data from the prevision-meteo URL and a main function to execute the processes in the Python code. The goal is to send asynchronous HTTP GET requests to retrieve temperatures and print the responses.
In the main and fetch functions, we use async with. In the fetch function, async with ensures that the connection is closed properly. In the main function, it ensures that the ClientSession is closed after completing the requests. These practices are important in asynchronous coding in Python to manage resources efficiently and prevent leaks.
In the last line of the main function, we use await asyncio.gather(*tasks). In our case, it runs all tasks concurrently, allowing the program to send multiple HTTP requests simultaneously. Using await ensures that the program waits for all tasks to complete before proceeding.
Output:
Temperature at marseille: 25 C Temperature at toulouse: 24 C Temperature at paris: 18 C Program executed in 5.86 seconds.
Code:
import requests import time def fetch(city): url = f"https://www.prevision-meteo.ch/services/json/{city}" response = requests.get(url) data = response.json() print(f"Temperature at {city}: {data['current_condition']['tmp']} C") def main(): cities = ['paris', 'toulouse', 'marseille'] for city in cities: fetch(city) start_time = time.time() main() print(f"Program executed in {time.time() - start_time:.2f} seconds.")
Output:
Temperature at Paris: 18 C Temperature at Toulouse: 24 C Temperature at Marseille: 25 C Program executed in 9.01 seconds.
The asynchronous model performs best when:
There are a large number of tasks, ensuring at least one task can always progress.
Tasks involve significant I/O, causing an asynchronous program to waste lots of time blocking when other tasks could be running.
Tasks are largely independent, minimizing inter-task communication (and thus for one task to wait upon another).
In this tutorial, we covered:
The concepts of asynchronous programming and related concepts.
Effective use of async/await.
Making asynchronous HTTP requests with aiohttp.
The benefits of asynchronous programming.
Thanks for reading. The second part will cover asynchronous programming with Django.
Python Documentation: Coroutines and Tasks
Python Documentation: asyncio - Asynchronous I/O
aiohttp Documentation
Aio Libraries
Concurrency vs Parallelism
以上是使用 Asyncio 進行非同步編程的詳細內容。更多資訊請關注PHP中文網其他相關文章!