0Pricing
Python Academy · レッスン

await、タスク、ギャザリング

並行処理に await、asyncio.create_task、asyncio.gather を使います。

「await、タスク、ギャザリング」はCoddyKit上の無料Python Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPython Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Python Academyコースには全4レッスンが含まれています。

awaitと逐次実行の比較

awaitだけを使うと、コルーチンは逐次実行されます。並行して実行するには、Taskでラップしてください。

import asyncio

async def main():
    # Sequential — total ~3 s
    await asyncio.sleep(2)
    await asyncio.sleep(1)

    # Concurrent using gather — total ~2 s
    await asyncio.gather(asyncio.sleep(2), asyncio.sleep(1))

asyncio.create_task()

asyncio.create_task(coro)は、コルーチンを現在のコルーチンと並行して独立したTaskとして実行するようスケジュールします。

import asyncio

async def work(n):
    await asyncio.sleep(n)
    return n

async def main():
    t1 = asyncio.create_task(work(2))
    t2 = asyncio.create_task(work(1))
    r1 = await t1
    r2 = await t2
    print(r1, r2)   # 2 1 (total ~2 s)

asyncio.run(main())

asyncio.gather()

asyncio.gather(*coros)はコルーチンを並行して実行し、すべてが完了すると結果を順序どおりに返します。

import asyncio

async def fetch(i):
    await asyncio.sleep(1)
    return f"data-{i}"

async def main():
    results = await asyncio.gather(fetch(1), fetch(2), fetch(3))
    print(results)  # ["data-1", "data-2", "data-3"] in ~1 s

asyncio.run(main())

return_exceptionsを指定したgather

return_exceptions=Trueを設定すると、失敗したコルーチンは例外を結果として返します。そのため、gather全体がキャンセルされることを防げます。

import asyncio

async def risky(n):
    if n == 2: raise ValueError("bad")
    return n

async def main():
    results = await asyncio.gather(
        risky(1), risky(2), risky(3),
        return_exceptions=True
    )
    print(results)   # [1, ValueError("bad"), 3]

asyncio.wait()

asyncio.wait(tasks, return_when=...)は、doneとpendingという2つの集合を返します。最初の結果に反応するにはFIRST_COMPLETEDを使用します。

import asyncio

async def slow(): await asyncio.sleep(3); return "slow"
async def fast(): await asyncio.sleep(1); return "fast"

async def main():
    tasks = {asyncio.create_task(slow()), asyncio.create_task(fast())}
    done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
    for t in pending: t.cancel()

タスクのキャンセル

キャンセルを要求するにはtask.cancel()を呼び出します。タスクは次のawait地点でasyncio.CancelledErrorを受け取ります。

import asyncio

async def long_job():
    try:
        await asyncio.sleep(100)
    except asyncio.CancelledError:
        print("Cancelled!")
        raise   # re-raise is required

async def main():
    t = asyncio.create_task(long_job())
    await asyncio.sleep(1)
    t.cancel()
    await t

asyncio.timeout() — Python 3.11以降

asyncio.timeout(seconds)でawaitを囲むと、処理に時間がかかりすぎた場合にキャンセルできます。

import asyncio

async def main():
    try:
        async with asyncio.timeout(2):
            await asyncio.sleep(10)   # too slow
    except TimeoutError:
        print("Timed out!")

asyncio.wait_for()

Python 3.11未満では、asyncio.wait_for(coro, timeout=N)を使用してタイムアウトを追加します。タイムアウトになるとasyncio.TimeoutErrorが発生します。

import asyncio

async def slow(): await asyncio.sleep(5)

async def main():
    try:
        await asyncio.wait_for(slow(), timeout=2)
    except asyncio.TimeoutError:
        print("Timed out")

タスクの名前付けと検査

asyncio.create_task(coro, name="...")を使用してタスクに名前を付けると、デバッグしやすくなります。名前はtask.get_name()で確認できます。

import asyncio

async def worker(): await asyncio.sleep(1)

async def main():
    t = asyncio.create_task(worker(), name="worker-1")
    print(t.get_name())   # worker-1
    await t

TaskGroup — Python 3.11以降

asyncio.TaskGroupは、タスクを構造化されたグループとして作成します。いずれかのタスクで例外が発生すると、すべてのタスクがキャンセルされます。

import asyncio

async def main():
    async with asyncio.TaskGroup() as tg:
        t1 = tg.create_task(asyncio.sleep(1))
        t2 = tg.create_task(asyncio.sleep(2))
    # all tasks done here

レート制限のためのセマフォ

asyncio.Semaphore(n)を使用すると、同時に実行できる処理の数(API呼び出しなど)を制限できます。

import asyncio

sem = asyncio.Semaphore(5)  # max 5 concurrent

async def limited_fetch(url):
    async with sem:
        await fetch(url)   # at most 5 at a time

確認問題

await coro()とasyncio.create_task(coro())の違いは何ですか。

まとめ

並行実行にはcreate_taskまたはgatherを使用します。gatherはすべての結果を収集し、waitはきめ細かな制御を可能にします。タスクは.cancel()でキャンセルし、wait_forまたはasyncio.timeoutでタイムアウトを設定します。

よくある質問

「await、タスク、ギャザリング」レッスンは無料ですか?

はい。「await、タスク、ギャザリング」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Python Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Python Academyコースには全4レッスンが含まれています。

「await、タスク、ギャザリング」で何を学びますか?

並行処理に await、asyncio.create_task、asyncio.gather を使います。 ブラウザで直接実行するハンズオンコードでPython Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Python Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのPython Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「await、タスク、ギャザリング」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このPython Academyレッスンでコードを書いて実行できますか?

はい。すべてのPython Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. コルーチンとイベントループ
  2. await、タスク、ギャザリング
  3. 非同期コンテキストマネージャーとイテレーター
  4. 本番環境での非同期 I/O パターン
← Python Academyに戻る