プログラミングの世界では、「ノンブロッキング」の概念が浸透しています。 JavaScript 開発者は、「非同期」という用語をよく使用します。これは、JavaScript の強みの 1 つであるためです。ただし、非同期プログラミングを真に理解するには、同時プログラミングと並列プログラミングの概念を理解することが不可欠です。
複数の独立したエンティティが同時に動作している場合、プログラミングは並行して行われます。これらのタスクがまったく同時に実行されているとは限りません。代わりに、CPU 時間などのリソースを共有することで、タスクが時間の経過とともに進歩していることを意味します。同時プログラミングの主な利点はその堅牢性です。1 つのプロセスがクラッシュしても、プログラムの残りの部分は機能し続けます。
アルゴリズムがその作業を複数の部分に分割できる場合、それは並列です。プロセッサの数が多いほど、並列処理のメリットが大きくなります。効率的な並列プログラミングにより、最新のマシンのリソースが最適化され、パフォーマンスが向上します。
同時実行の例:
肉を焼いてソースを作る必要がある食事を準備していると想像してください。まずはバーベキューに肉を乗せることから始まります。肉を焼いている間に、ソース用のトマトやその他の野菜を切ります。そして、時々肉の様子を見ながらタレを煮始めます。ここでは、両方の作業 (肉を焼くこととソースを作ること) が進行中ですが、あなたはそれらの間で注意を切り替えています。これは同時実行性を表します。
並列処理の例:
さて、あなたを助けてくれる友人がいるとしましょう。あなたが肉を焼くのに集中している間、友人はソース作りを担当します。両方のタスクは、注意を切り替える必要がなく、同時に実行されます。これは並列性を表します。
非同期プログラミングには、ユーザー入力、端末への出力、ソケットからの読み取り、ディスクへの書き込みなど、プログラムの外部で発生する入出力 (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())
この非同期プログラムでは、タスクが同時に実行されるため、合計の実行時間が短縮されます。非同期プログラミングのコンポーネントを見てみましょう。
イベント ループ、コルーチン、フューチャーは、非同期 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 中国語 Web サイトの他の関連記事を参照してください。