見出し画像

Pythonの 非同期処理ライブラリ asyncio の使い方

Pythonの 非同期処理ライブラリ「asyncio」の使い方をまとめました。


1. asyncio

asyncio」は、Pythonの非同期処理ライブラリです。

2. 同期処理 と 非同期処理

同期処理」では、タスクは順番に実行され、1つのタスクが完了するまで次のタスクは待たされます。これは直感的ですが、リソースを無駄にする可能性があります。たとえば、「ファイルアクセス」や「通信処理」のレスポンスを待つ間、他のタスクを実行できません。

一方、「非同期処理」は、時間のかかるタスクを実行している間に、他のタスクを実行することができます。これにより、効率的にリソースを利用し、アプリケーションの応答性を向上させることができます。

3. Colabで必要な前処理

Colabでは「asyncio」をインポートする前に、以下の処理が必要です。

# Colabで必要な前処理
import nest_asyncio
nest_asyncio.apply()

「asyncio」はデフォルトでイベントループのネストを許可していませんが、Colabはすでにイベントループが開始済みのため、これなしに「asyncio」を使用すると、次のようなエラーが表示されます。

RuntimeError: asyncio.run() cannot be called from a running event loop

4. コルーチンの実行

コルーチン」は「async」で定義する「非同期関数」です。「await」で他の「非同期関数」を呼び出すことができ、その結果が返えるまで実行を一時停止します。コルーチン自体はただの定義で、「イベントループ」によって実行する必要があります。

4-1. コルーチンの実行

「コルーチン」の実行例は、次のとおりです。

import asyncio

async def main():  # mainコルーチンの定義
    print('hello')
    await asyncio.sleep(1)  # 1秒スリープのコルーチン
    print('world')

asyncio.run(main()) # mainコルーチンの実行

「hello」と表示された1秒後に「world」が表示されます。

hello
world

asyncio.run() でイベントループからコルーチンを実行しています。asyncio.sleep()は、任意の秒数スリープするコルーチンになります。

4-2. コルーチンの直列実行

「コルーチン」の直列実行の例は、次のとおりです。
say_after()を順番に「await」で呼び出しています。

import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

async def main():
    print(f"started at {time.strftime('%X')}")

    await say_after(1, 'hello')
    await say_after(2, 'world')
    
    print(f"finished at {time.strftime('%X')}")

asyncio.run(main())

say_after(1, 'hello')に1秒、say_after(2, 'world')に2秒、合計3秒かかりました。

started at 04:16:32
hello
world
finished at 04:16:35

5. タスクの実行

タスク」は、コルーチンの実行状態(実行中、完了、キャンセルなど)を管理するためのオブジェクトです。asyncio.create_task() でコルーチンからタスクを作成することができます。コルーチンは直接キャンセルできませんが、タスク経由でキャンセルできます。

5-1. タスクの並列実行

「タスク」の並列実行の例は、次のとおりです。

async def main():
    task1 = asyncio.create_task(
        say_after(1, 'hello'))

    task2 = asyncio.create_task(
        say_after(2, 'world'))

    print(f"started at {time.strftime('%X')}")

    await task1
    await task2

    print(f"finished at {time.strftime('%X')}")

asyncio.run(main())

say_after(1, 'hello')に1秒、say_after(2, 'world')に2秒、並列実行のため2秒で完了します。

started at 04:32:03
hello
world
finished at 04:32:05

5-2. asyncio.gather()によるタスクの並列実行

asyncio.gather()にコルーチンを渡すことでも、タスクを並列実行することができます。自動的にタスクとしてスケジュールされます。コードを簡潔にかけますが、タスクのキャンセルなどはできません。

asyncio.gather()によるタスクの並列実行の例は、次のとおりです。

async def main():
    print(f"started at {time.strftime('%X')}")

    await asyncio.gather(
        say_after(1, 'hello'),
        say_after(2, 'world'),
    )

    print(f"finished at {time.strftime('%X')}")

asyncio.run(main())

say_after(1, 'hello')に1秒、say_after(2, 'world')に2秒、並列実行のため2秒で完了します。

started at 04:16:42
hello
world
finished at 04:16:44



この記事が気に入ったらサポートをしてみませんか?