Python 的 asyncio.gather 和 TaskGroup 有什么区别?子任务失败后会发生什么?
下面是一段教学模拟,不是真实面试记录。
🧑💻 面试官:三个并发请求里,一个报错了,另两个会怎样?
🙋♂️ 我:gather 抛异常,其他请求也就停止了。
🧑💻 面试官:抛异常给调用者,和取消已经启动的任务,是同一件事吗?
🙋♂️ 我:不是,其他任务可能还在运行。
🧑💻 面试官:那 TaskGroup 为什么要把取消和等待清理也纳入任务组的管理?
要分开看「谁先收到异常」和「剩下的任务谁来收尾」。并发聚合结束,不代表所有子任务都已经停止。
面试速答(60 秒版)
gather 用来并发等待一组任务并收集结果。默认情况下,一个任务抛出异常,会将首个异常传播给调用者,但不会因此自动取消其他任务,它们可能继续执行。
TaskGroup 是 Python 3.11 起提供的任务组管理方式。一个子任务出现非取消异常时,通常会取消其他子任务,并等待它们结束清理,再把相关错误组织成异常组向外传播。
所以,需要独立收集各项结果时,可以考虑 gather;一组任务共同承担一个工作、失败后需要统一收尾时,TaskGroup 更容易表达这种生命周期。
取消仍是协作式的,也不会撤销已经发生的外部写入。选型时还要明确错误、取消与业务补偿的边界。

知识点详解:一个任务失败,其他任务还欠什么工作?
gather 的默认异常,不等于全体取消
假设 A 很快失败,B、C 还在查询。
默认 gather 将 A 的异常传给调用者,调用者可以进入异常处理。但 B、C 不会因为这个子任务异常就自动被取消。
这意味着我们不能捕获异常后立即关闭它们正在使用的共享资源,或者直接宣布“所有请求已经结束”。
还要区分调用者主动取消 gather 的情况:取消聚合等待与子任务自身报错,不是同一种路径。具体传播规则应查对应版本。asyncio 官方说明

TaskGroup 把启动和收尾放在同一个范围
TaskGroup 在一个异步上下文里创建和管理子任务。退出范围前,它需要处理任务组中的工作,而不是把仍然运行的任务留给不明确的调用者。
某个子任务产生非取消异常时,它会启动剩余任务的取消,并等待清理完成,然后传播相关错误。多个失败可能通过 ExceptionGroup 表达,不只是丢掉其他错误、保留第一条。
这里讲的是通常的失败路径;KeyboardInterrupt、SystemExit 等具有特殊处理,不应套用普通错误组的全部结论。
因此,对“一项失败就不再需要其余结果”的任务组,它能更清楚地约束生命周期。它不是因为名字里有 Group,就能把所有业务写入变成一个事务。
return_exceptions=True:结果里面也可能是错误
使用 gather 的 return_exceptions=True 时,异常可以作为返回列表中的元素,而不是按默认方式立即传播。
假设分别查询三家来源,我们允许部分失败,仍想展示其他来源结果。这种方式可能合适,但拿到列表后必须逐项区分结果和异常。
不能把长度为三的返回列表记成“三个请求全部成功”,也不能把异常字符串直接当成查询内容交给模型总结。
是否容许部分失败,是业务规则;配置只提供收集方式,不替我们决定哪些结果足够继续。
取消请求,不是撤回所有工作
任务接到取消时,应在 finally 等清理路径释放资源。不要随意吞掉 CancelledError,让任务继续运行却让外层以为已经收尾。
不过,外部系统可能已经接受请求。取消本地等待,不保证数据库写入或付款没有发生。需要可靠结果时,还要用业务标识查询状态、幂等重试或补偿。
TypeScript 的 Promise.all 可以聚合异步结果,但一个 Promise 拒绝,不会自动取消其他已经开始的工作。JavaScript 没有一套可直接替换 Python TaskGroup 的内置同名 API;若使用 AbortController,需要各个操作实际支持并响应取消。Promise.all 说明
因此不能把 Promise.all 称为完全等价的 TaskGroup。本文比较机制,不机械提供不存在的双语言接口。
面试官继续追问
三个查询允许一个失败,应该选择哪个?
可以用 gather 收集各项结果并明确错误处理,也可以设计其他管理方式。关键是让“部分成功”的规则清晰,而不是默认一个异常就丢掉所有有效结果。
TaskGroup 能保证子任务立即停止吗?
不能。取消需要任务配合,清理也可能耗时。阻塞代码或不正确处理取消的任务,仍会拖住生命周期。
为什么一个教学实验显示 gather 的其他任务也取消了?
可能是最外层事件循环退出时清理了剩余任务,而不是 gather 因某个子任务异常主动取消了兄弟任务。实验应保持循环运行,观察任务状态,再区分这两种原因。
面试速记卡
- gather 默认:首个子任务异常向外传播,其他任务不会因此自动取消。
- return_exceptions:将异常作为结果项收集,仍需逐项判断成功。
- TaskGroup:管理任务组生命周期,失败时取消兄弟任务并等待清理。
- 取消:协作式停止,不等于撤销外部副作用。
- 跨语言:Promise.all 不自动取消其他任务,不是完整 TaskGroup 对应物。
公司面试真题
这道题暂未收录可核验的公司真题来源。你可以先阅读本文解析,或浏览已收录的公司面试真题。
浏览公司面试真题 →