科技 · 實用指南
asyncio.gather 已拋錯但兄弟任務仍在跑?選擇 TaskGroup 或明確取消
gather傳播第一個例外但不取消兄弟任務。需要一起失敗時用TaskGroup;選擇性策略則持有Task、取消並等待清理。

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建立兄弟任務;首個非取消例外會取消其餘任務並等待清理。
在async with外用except*處理ExceptionGroup,不要吞掉CancelledError。
4. 選擇性政策要持有、取消、等待、重拋
先create_task保留句柄,例外時只取消政策指定任務,再以return_exceptions=True等待所有已擁有任務。
清理後重新拋出原錯誤,或明確表示每個結果。
5. 驗收協作式取消
CancelledError在await點送入;以try/finally清理並通常重新拋出。
驗證gather繼續、TaskGroup取消等待、手動耗盡和父任務取消,並檢查副作用。
