テクノロジー · 実用ガイド

asyncio.gatherが例外を出しても兄弟Taskが動く?TaskGroupか所有Taskの取消を選ぶ

gatherは最初の例外を伝えるが兄弟を取消さない。一緒に失敗させるならTaskGroup、選択的ならTaskを保持して取消と終了待ちを行う。

TaskGroupの全体失敗か所有Taskの選択的取消かを選ぶ図。
オリジナルイラスト:先に失敗方針を決めてからasyncio APIを選ぶ。 Watch at Ease

asyncio.gatherは集約であり全員失敗のスコープではない。最初の例外が呼出側へ届いても未完了の兄弟は走る。捕捉時点でgather Futureが完了済みならcancelはfalseである。

実行前に失敗方針を決める。同一整合性境界ならPython 3.11+のTaskGroupを使い、選択的ならTaskを自分で作り取消と清掃待ちを行う。

1. 失敗後も動くことを再現

短時間で失敗する協程と後で印を残す協程をgatherする。例外後にcancelしループを継続し、falseと印を確認する。

CPython 3.12.14では遅い兄弟が完了し、TaskGroup版では取消された。

2. APIより先に方針を選ぶ

一つの論理結果を共同更新し一つの失敗で残りが無効ならTaskGroupを使う。

独立作業や回復可能な失敗ならTaskの所有権を保持する。

3. Python 3.11+はTaskGroup

同じTaskGroupから全兄弟を作る。最初の非取消例外で残りを取消し清掃後にExceptionGroupを出す。

async with外でexcept*を使いCancelledErrorは飲み込まない。

4. 選択的方針は所有、取消、待機、再送出

create_taskで句柄を保持し、対象だけcancelする。return_exceptions=Trueで全所有Taskの清掃を待つ。

待機後は元の例外を再送出するか結果を明示する。

5. 協調取消を試験

CancelledErrorはawait点で入る。try/finallyで資源を解放し通常は再送出する。

gather継続、TaskGroup取消、手動待機、親の外部取消と副作用記録を確認する。

gatherが先に例外を出し兄弟が後で完了する時間軸。
オリジナル技術イラスト:完了済み集約の取消は遅すぎる。 Watch at Ease