await会抛出第一个异常,其余异常被静默丢弃;需用except*捕获exceptiongroup并遍历eg.exceptions获取所有原始异常,低版本可用gather(return_exceptions=true)但不取消任务。

asyncio.TaskGroup 里多个任务出错,await 会抛哪个异常?
只会抛出第一个被触发的异常,其余异常被静默丢弃——这是最常踩的坑。Python 3.11+ 的 asyncio.TaskGroup 默认行为是“短路失败”,哪怕你启动了 10 个并发任务,只要第一个出错,后续任务会被取消,且其他任务的异常根本不会暴露出来。
实操建议:
- 必须用
except*(异常组捕获)来拿到所有异常,而不是except -
TaskGroup内部异常会被包装成ExceptionGroup,不是单个异常对象 - 如果只写
except Exception:,会漏掉所有非首个异常,连日志都打不出来
怎么用 except* 拿到每个任务的具体错误?
except* 是 Python 3.11 引入的专用语法,专为 ExceptionGroup 设计。它能匹配异常组中「所有匹配子类型」的异常,而不是只抓最外层包装。
示例:
import asyncio
<p>async def fails_fast():
raise ValueError("快失败")</p><p>async def fails_slow():
await asyncio.sleep(0.1)
raise TypeError("慢失败")</p><p>async def main():
try:
async with asyncio.TaskGroup() as tg:
tg.create_task(fails_fast())
tg.create_task(fails_slow())
except<em> ValueError as eg:
print("捕获到 ValueError:", eg.exceptions)
except</em> TypeError as eg:
print("捕获到 TypeError:", eg.exceptions)
except* Exception as eg:
print("其他异常:", eg.exceptions)</p><p>asyncio.run(main())
</p>
注意:eg.exceptions 是一个元组,每个元素是原始异常实例,可直接 repr() 或 traceback.print_exception() 输出。
不升级 Python 3.11+ 怎么兼容处理多异常?
低版本没有 TaskGroup 和 except*,只能手动聚合异常。常见替代方案是用 asyncio.gather(..., return_exceptions=True),但它返回的是结果列表,异常以 Exception 实例形式混在其中,需要逐个判断。
关键差异点:
-
gather(return_exceptions=True)不会取消其他任务(而TaskGroup会),适合“尽力执行”场景 - 返回值类型统一为
List[Any],需用isinstance(x, Exception)过滤 - 无法区分“任务未开始”和“任务已抛异常”,也不提供任务取消状态
示例片段:
results = await asyncio.gather(
fails_fast(), fails_slow(),
return_exceptions=True
)
for i, r in enumerate(results):
if isinstance(r, Exception):
print(f"任务 {i} 失败:{r}")
为什么 ExceptionGroup 不能用 str(e) 简单打印?
直接 print(e) 只显示顶层摘要,比如 ExceptionGroup: 2 exceptions, 1 sub-exception,看不到具体堆栈。更糟的是,某些日志库(如 logging.exception())默认不递归展开 ExceptionGroup。
安全做法:
- 用
traceback.print_exception(type(e), e, e.__traceback__)—— 它能自动递归打印所有子异常 - 或手动遍历
e.exceptions,对每个子异常调用traceback.print_exception() - 别依赖
str(e)或e.args[0]做错误分类,它们不包含子异常信息
真正麻烦的地方不在语法,而在异常生命周期:任务被取消时抛 CancelledError,但 TaskGroup 会把它吞掉、不放进 ExceptionGroup —— 所以想确认哪些任务被取消,得靠 task.cancelled() 状态检查,而不是看异常类型。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











