Watch at Ease看见世界,读懂生活。

科技 · 实用指南

asyncio.gather 已抛错但兄弟任务仍在运行?选择 TaskGroup 或显式取消

gather传播首个异常但不会取消兄弟任务。需要一起失败时用TaskGroup;需要选择性策略时持有Task句柄、取消并等待清理。

选择TaskGroup一起失败或显式持有Task进行选择性取消的决策图。
原创插画:先决定失败策略,再选择asyncio协调原语。 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建立兄弟任务;首个非取消异常会取消其余任务并等待清理。

在async with外用except*处理ExceptionGroup,不要吞掉CancelledError。

4. 选择性策略要持有、取消、等待、重抛

先create_task保留句柄,异常时只取消策略指定的任务,再用return_exceptions=True等待所有已拥有任务。

清理完成后重新抛出原错误,或明确编码每个结果。

5. 验收协作式取消

取消在await点注入CancelledError;以try/finally清理并通常重新抛出。

验证gather继续、TaskGroup取消等待、手动耗尽任务和父任务外部取消,并核对副作用日志。

gather先抛错、兄弟任务后完成以及延迟cancel返回false的时间线。
原创技术插画:聚合Future完成后取消无效,必须等待子任务清理。 Watch at Ease