高性能 Python: Asyncio

Susan Sarandon
Susan Sarandonオリジナル
2025-01-01 06:30:09939ブラウズ

High-Performance Python: Asyncio

同時実行プログラミングは、複数のタスクの同時実行を扱うプログラミング アプローチです。 Python では、asyncio は非同期プログラミングを実装するための強力なツールです。コルーチンの概念に基づいて、asyncio は I/O 集中型のタスクを効率的に処理できます。この記事では、asyncio の基本原理と使用法を紹介します。

High-Performance Python: Asyncio

asyncio が必要な理由

I/O 操作を処理する場合、マルチスレッドを使用すると、通常の単一スレッドと比較して効率が大幅に向上することがわかっています。では、なぜ依然として asyncio が必要なのでしょうか?

マルチスレッドには多くの利点があり、広く使用されていますが、次のような制限もあります。

  • たとえば、マルチスレッドの実行プロセスは中断されやすいため、競合状態の状況が発生する可能性があります。
  • さらに、スレッドの切り替え自体にも一定のコストがかかり、スレッド数は無制限に増やすことはできません。したがって、I/O 操作が非常に重い場合、マルチスレッドでは高効率と高品質の要件を満たせない可能性があります。

asyncio はまさにこれらの問題を解決するために登場しました。

同期 VS 非同期

まず、Sync (同期) と Async (非同期) の概念を区別しましょう。

  • 同期とは、操作が次々に実行されることを意味します。次の操作は、前の操作が完了した後にのみ実行できます。
  • 非同期とは、異なる操作を交互に実行できることを意味します。いずれかの操作がブロックされた場合、プログラムは待機せず、実行可能な操作を見つけて続行します。

asyncio の仕組み

  1. コルーチン: asyncio はコルーチンを使用して非同期操作を実現します。コルーチンは、async キーワードで定義された特別な関数です。コルーチンでは、await キーワードを使用して、現在のコルーチンの実行を一時停止し、非同期操作が完了するのを待つことができます。
  2. イベント ループ: イベント ループは、asyncio のコア メカニズムの 1 つです。コルーチンのスケジュールと実行、およびコルーチン間の切り替えの処理を担当します。イベント ループは、実行可能なタスクを常にポーリングします。タスクの準備が完了すると (I/O 操作が完了したときやタイマーが期限切れになったときなど)、イベント ループによってそのタスクが実行キューに入れられ、次のタスクに進みます。
  3. 非同期タスク: asyncio では、非同期タスクを作成することでコルーチンを実行します。非同期タスクは、asyncio.create_task() 関数によって作成されます。この関数は、コルーチンを待機可能なオブジェクトにカプセル化し、それを処理のためにイベント ループに送信します。
  4. 非同期 I/O 操作: asyncio は、一連の非同期 I/O 操作 (ネットワーク リクエスト、ファイルの読み取りと書き込みなど) を提供します。これは、コルーチンやイベント ループとシームレスに統合できます。キーワードを待ちます。非同期 I/O 操作を使用すると、I/O 完了待機中のブロックが回避され、プログラムのパフォーマンスと同時実行性が向上します。
  5. コールバック: asyncio は、非同期操作の結果を処理するためのコールバック関数の使用もサポートしています。 asyncio.ensure_future() 関数を使用すると、コールバック関数を待機可能なオブジェクトにカプセル化し、処理のためにイベント ループに送信できます。
  6. 同時実行: asyncio は複数のコルーチン タスクを同時に実行できます。イベント ループは、タスクの準備状況に応じてコルーチンの実行を自動的にスケジュールし、効率的な同時プログラミングを実現します。

要約すると、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() メソッドは、コルーチンをイベント ループに追加して実行します。

非同期 I/O 操作

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() メソッドを呼び出して Web ページのコンテンツを取得して印刷します。

: ここでは、requests ライブラリの代わりに aiohttp を使用します。これは、aiohttp ライブラリには互換性があるのに対し、requests ライブラリには asyncio との互換性がないためです。 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())

この例では、2 つのコルーチン タスク task1() と task2() を定義し、どちらも時間のかかる操作を実行します。コルーチン main() は、asyncio.gather() を通じてこれら 2 つのタスクを同時に開始し、それらが完了するのを待ちます。同時実行により、プログラムの実行効率が向上します。

選び方は?

実際のプロジェクトでは、マルチスレッドと非同期のどちらを選択すべきでしょうか?大物がそれを鮮やかに要約しました:

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())
  • I/O バウンドで I/O 操作が遅く、多くのタスク/スレッドの連携が必要な場合は、asyncio を使用する方が適切です。
  • I/O バウンドであるが、I/O 操作が高速で、限られた数のタスク/スレッドのみが必要な場合は、マルチスレッドで十分です。
  • CPU バウンドの場合、プログラムの実行効率を向上させるためにマルチ処理が必要です。

練習する

リストを入力します。リスト内の各要素について、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 秒かかります

concurrent.futures を使用した非同期実装

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 でマルチプロセスの同時実行性を実装するためのライブラリです。いくつかの違いがあります:

  1. インターフェイスベースのカプセル化: concurrent.futures.ProcessPoolExecutor は、concurrent.futures モジュールによって提供される高レベルのインターフェイスです。基礎となるマルチプロセス関数をカプセル化し、マルチプロセス コードの作成を容易にします。マルチプロセッシングは Python の標準ライブラリの 1 つであり、完全なマルチプロセスのサポートを提供し、プロセスに対する直接操作を可能にします。
  2. API の使用法: concurrent.futures.ProcessPoolExecutor の使用法は、スレッド プールの使用法と似ています。呼び出し可能なオブジェクト (関数など) を実行のためにプロセス プールに送信し、実行結果を取得するために使用できる Future オブジェクトを返します。マルチプロセッシングは、より低レベルのプロセス管理および通信インターフェイスを提供します。プロセスは明示的に作成、開始、制御でき、複数のプロセス間の通信はキューまたはパイプを使用して実行できます。
  3. スケーラビリティと柔軟性: マルチプロセッシングはより多くの低レベル インターフェイスを提供するため、concurrent.futures.ProcessPoolExecutor と比較して柔軟性が高くなります。プロセスを直接操作することで、プロセスの優先順位の設定やプロセス間でのデータの共有など、プロセスごとのきめ細かい制御が可能になります。 concurrent.futures.ProcessPoolExecutor は、単純なタスクの並列化により適しており、多くの基礎となる詳細を隠し、マルチプロセス コードの作成を容易にします。
  4. クロスプラットフォーム サポート: concurrent.futures.ProcessPoolExecutor と multiprocessing は両方とも、クロスプラットフォーム マルチプロセス サポートを提供し、さまざまなオペレーティング システムで使用できます。

要約すると、concurrent.futures.ProcessPoolExecutor は、基礎となるマルチプロセス関数をカプセル化する高レベルのインターフェイスであり、単純なマルチプロセス タスクの並列化に適しています。 multiprocessing はより低レベルのライブラリであり、より詳細な制御と柔軟性を提供し、プロセスのきめ細かい制御が必要なシナリオに適しています。特定の要件に応じて適切なライブラリを選択する必要があります。単純なタスクの並列化だけの場合は、concurrent.futures.ProcessPoolExecutor を使用してコードを簡素化できます。さらに低レベルの制御と通信が必要な場合は、マルチプロセッシング ライブラリを使用できます。

まとめ

マルチスレッドとは異なり、asyncio はシングルスレッドですが、内部イベント ループのメカニズムにより、複数の異なるタスクを同時に実行でき、マルチスレッドよりも優れた自律制御が可能です。

asyncio のタスクは動作中に中断されないため、競合状態の状況は発生しません。

特に大量の I/O 操作を伴うシナリオでは、asyncio はマルチスレッドよりも操作効率が高くなります。 asyncio でのタスク切り替えのコストはスレッド切り替えのコストよりもはるかに小さく、asyncio が開始できるタスクの数はマルチスレッドのスレッド数よりもはるかに多いためです。

ただし、多くの場合、asyncio を使用するには、前の例の aiohttp など、特定のサードパーティ ライブラリのサポートが必要であることに注意してください。また、I/O 操作が高速で重くない場合は、マルチスレッドを使用すると問題を効果的に解決できます。

  • asyncio は、非同期プログラミングを実装するための Python ライブラリです。
  • コルーチンは asyncio の中核概念であり、async および await キーワードを通じて非同期操作を実現します。
  • asyncio は、非同期 I/O 操作用の強力な API を提供し、I/O 集中型のタスクを簡単に処理できます。
  • asyncio.gather() などのメカニズムを通じて、複数のコルーチン タスクを同時に実行できます。

Leapcell: FastAPI、Flask、その他の Python アプリケーションに最適なプラットフォーム

最後に、Flask/FastAPI をデプロイするための理想的なプラットフォームである Leapcell を紹介します。

Leapcell は、最新の分散アプリケーション向けに特別に設計されたクラウド コンピューティング プラットフォームです。従量課金制の料金モデルにより、アイドルコストは発生しません。つまり、ユーザーは実際に使用したリソースに対してのみ料金を支払います。

High-Performance Python: Asyncio

  1. 多言語サポート
    • JavaScript、Python、Go、または Rust での開発をサポートします。
  2. 無制限のプロジェクトを無料で展開
    • 使用量に基づいてのみ請求します。リクエストがない場合は無料です。
  3. 比類のない費用対効果
    • アイドル料金なしの従量課金制です。
    • たとえば、25 ドルで 694 万件のリクエストをサポートでき、平均応答時間は 60 ミリ秒です。
  4. 簡素化された開発者エクスペリエンス
    • 直感的なユーザー インターフェイスで簡単にセットアップできます。
    • 完全に自動化された CI/CD パイプラインと GitOps の統合。
    • リアルタイムのメトリクスとログにより、実用的な洞察が得られます。
  5. 簡単な拡張性と高いパフォーマンス
    • 自動スケーリングにより、高い同時実行性を簡単に処理できます。
    • 操作オーバーヘッドがゼロなので、開発者は開発に集中できます。

ドキュメントで詳細を確認してください!
Leapcell Twitter: https://x.com/LeapcellHQ

以上が高性能 Python: Asyncioの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。