taskgroup.cancel_scope 不会吞掉异常,而是传播首个异常后取消其余任务,后续异常被静默丢弃;其抛出的是exceptiongroup(3.11+)或baseexceptiongroup(3.12+),需用except*捕获。

TaskGroup.cancel_scope 会吞掉异常吗?
不会吞,但默认行为是“传播首个异常后取消其余任务”,后续异常被静默丢弃。这是 asyncio.TaskGroup 的设计逻辑,不是 bug。如果你看到只有第一个 Exception 被抛出,其余任务的错误没出现,不是漏了 try/except,而是 TaskGroup 主动压制了它们。
实操建议:
- 不要在每个子任务里单独
try/except后吞异常——这会让错误不可见,也破坏了TaskGroup的协作取消机制 - 想收集全部异常?必须手动保存:在每个子任务中用
except Exception as e:捕获并追加到共享列表(如errors.append(e)),再在with TaskGroup() as tg:外统一处理 - 注意:
TaskGroup本身只抛出第一个异常,且该异常类型是ExceptionGroup(Python 3.11+)或BaseExceptionGroup(3.12+),不是原始异常类型
为什么 await tg.create_task(...) 里抛异常不进 except?
因为 tg.create_task(...) 返回的是一个 Task 对象,不是协程对象;异常发生在 task 内部调度时,不是在 create_task 调用时。所以你在 create_task 外层写 try/except 是捕不到的。
实操建议:
- 异常只能在
with TaskGroup() as tg:的__exit__阶段集中暴露,不是在create_task行 - 若需对单个任务做细粒度错误处理,应在传入的协程函数内部处理,例如:
async def worker(): try: ... except ValueError: log_error(); return - 别误以为
await tg.create_task(...)是合法写法——create_task不返回可 await 的协程,直接 await 会报RuntimeError: cannot reuse already awaited coroutine
Python 3.10 和 3.11+ 的异常处理差异在哪?
3.10 没有 TaskGroup,得用第三方库(如 anyio)或手写 asyncio.create_task + asyncio.gather(return_exceptions=True);3.11+ 原生 TaskGroup 强制使用 ExceptionGroup,而 gather(..., return_exceptions=True) 返回的是普通 list。
实操建议:
- 升级到 3.11+ 后,检查是否用了
except Exception:——它捕获不了ExceptionGroup,得显式写except* Exception:(注意星号) - 若需兼容旧版本,避免依赖
ExceptionGroup特性,改用gather(return_exceptions=True)并遍历结果判断isinstance(r, BaseException) -
except*只能用于ExceptionGroup及其子类,对普通异常无效;反过来,普通except也无法匹配ExceptionGroup
如何让 TaskGroup 抛出所有异常而不是只第一个?
原生 TaskGroup 不支持“抛出全部异常”,它的语义就是“失败即终止”。真要聚合所有错误,只能放弃 TaskGroup 的自动取消能力,改用 asyncio.gather(..., return_exceptions=True),或者自己维护任务集合 + asyncio.wait(..., return_when=asyncio.FIRST_EXCEPTION)。
实操建议:
- 如果业务要求“全量执行、全量上报错误”,就别用
TaskGroup,改用gather(return_exceptions=True),然后过滤出BaseException实例 - 若仍想保留
TaskGroup的结构优势,可在每个子任务末尾向共享queue.Queue或asyncio.Queue推送结果或异常,主协程在with块外消费队列 - 注意:
TaskGroup的__aexit__在所有任务结束(含取消)后才触发,所以异常聚合逻辑不能放在子任务里“等全部完成”——那会死锁
最常被忽略的一点:ExceptionGroup 不是 Exception 的子类,except Exception: 完全捕获不到它。哪怕你写了 try/except,没加 * 就等于没写。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











