メインコンテンツへスキップ
見出し画像

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



     
     

    npaka

     
     
    プログラマー。iPhone / Android / Unity / ROS / AI / AR / VR / RasPi / ロボット / ガジェット。年2冊ペースで技術書を執筆。アニソン / カラオケ / ギター / 猫 twitter : @npaka123

    あなたへのおすすめ