プログラミングの世界では、「ノンブロッキング」の概念が浸透しています。 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 プログラムの重要な要素です。
イベント ループ: タスクの切り替えと実行フローを管理し、非同期で実行されるタスクを追跡します。
Coroutines: 一時停止および再開できる特別な関数。待機中に他のタスクを実行できます。コルーチンは、関数内のどこでタスク切り替えイベントが発生し、制御をイベント ループに戻すかを指定します。コルーチンは通常、イベント ループによって作成され、内部的にタスク キューに保存されます。
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 キーワードに注目してください。
これらの関数は コルーチンになりました。
Python のコルーチン関数の前にはキーワード async が付きます。ここでの main() 関数は、async.gather メソッドを使用してすべてのタスクを実行するため、タスク コーディネーターまたは単一のイベント ループです。 asyncio.gather 関数は、待機可能なオブジェクトを同時に実行します。
出力:
Hello Hello Hello Program executed in 2.01 seconds.
各タスクが await asyncio.sleep(2) に達すると、次のタスクに進み、終了すると戻ってきます。それは、「2 秒間寝ます。別のことをしてください。」と言っているようなものです。
簡単に比較するために、同期バージョンを見てみましょう。
import time def task(): time.sleep(2) print("Hello") for _ in range(3): task()
上記のコードでは、Python での従来のプログラミング方法を実行しています。プロセスの実行にはさらに時間がかかることがわかります。
出力:
Hello Hello Hello Program executed in 6.01 seconds.
実行時間を確認できるようになりました。 time.sleep() はブロッキング タスク、asyncio.sleep() は非ブロッキングまたは長いタスクと考えてください。非同期プログラミングでは、asyncio.sleep() などの何かを待機することの利点は、周囲の関数が、すぐに実行できる状態になっている別の関数に一時的に制御を譲渡できることです。
Python での非同期プログラミングの基本的な例をいくつか理解したので、Python での非同期プログラミングのルールを調べてみましょう。
コルーチン: コルーチンは直接実行できません。コルーチン関数を直接実行しようとすると、コルーチン オブジェクトが返されます。代わりに、asyncio.run():
を使用してください。
import asyncio async def hello(): await asyncio.sleep(1) print('Hello') asyncio.run(hello())
待機可能オブジェクト: コルーチン、フューチャー、タスクが主な待機可能オブジェクトです。 Python コルーチンは待機可能であり、他のコルーチンから待機することができます。
Await キーワード:await は非同期関数内でのみ使用できます。
async def hello(): await asyncio.sleep(1) print("Hello")
互換性: すべての Python モジュールが非同期プログラミングと互換性があるわけではありません。たとえば、await asyncio.sleep() を time.sleep() に置き換えるとエラーが発生します。互換性とメンテナンスが行われているモジュールのリストはここで確認できます。
次のセクションでは、非同期プログラミングの一般的な使用法である HTTP リクエストについて説明します。
次のコードを見てみましょう:
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())
上記のコードでは、2 つの非同期関数を作成します。1 つは prevision-meteo URL からデータをフェッチする関数、もう 1 つは Python コード内のプロセスを実行する main 関数です。目的は、非同期 HTTP GET リクエストを送信して温度を取得し、応答を出力することです。
main 関数と fetch 関数では、async with を使用します。 fetch 関数では、async with を使用して接続が適切に閉じられるようにします。 main 関数では、リクエストの完了後に ClientSession が確実に閉じられるようにします。これらの実践は、リソースを効率的に管理し、リークを防ぐために、Python での非同期コーディングにおいて重要です。
main 関数の最後の行では、await asyncio.gather(*tasks) を使用します。この例では、すべてのタスクを同時に実行し、プログラムが複数の HTTP リクエストを同時に送信できるようにします。 await を使用すると、プログラムはすべてのタスクが完了するまで待機してから続行します。
出力:
Temperature at marseille: 25 C Temperature at toulouse: 24 C Temperature at paris: 18 C Program executed in 5.86 seconds.
コード:
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.")
出力:
Temperature at Paris: 18 C Temperature at Toulouse: 24 C Temperature at Marseille: 25 C Program executed in 9.01 seconds.
非同期モデルは次の場合に最高のパフォーマンスを発揮します:
多数のタスクがあり、少なくとも 1 つのタスクが常に進行できるようにしています。
タスクには大量の I/O が含まれるため、非同期プログラムは他のタスクの実行をブロックするために多くの時間を無駄にします。
タスクは大部分が独立しているため、タスク間の通信が最小限に抑えられます (したがって、あるタスクが別のタスクを待機することになります)。
このチュートリアルでは以下について説明しました:
非同期プログラミングの概念と関連する概念。
async/await の効果的な使用法。
aiohttp.
非同期プログラミングの利点。
読んでいただきありがとうございます。 2 番目のパートでは、Django を使用した非同期プログラミングについて説明します。
Python ドキュメント: コルーチンとタスク
Python ドキュメント: asyncio - 非同期 I/O
aiohttp ドキュメント
愛生図書館
同時実行と並列処理
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3