Python asyncio TaskGroup 如何收口异常:取消传播与部分结果处理
批量请求三个下游服务时,最麻烦的情况不是某一个请求失败,而是失败之后另外两个任务还在继续写入共享状态。Python 3.11 引入的 asyncio.TaskGroup 会把这件事收紧:一个子任务抛出未处理异常,其他兄弟任务会被取消,离开代码块时再以聚合异常的形式交给调用方。
要点速览
- TaskGroup 适合“一组任务要么一起完成、要么整体收口”的并发批次。
- 兄弟任务收到取消后,清理逻辑应放在
finally,不要吞掉CancelledError。 - 需要保留成功结果时,把结果写入批次上下文,并在外层用
except* Exception统一整理。 - 失败任务已经决定批次结果后,不要继续把半成品标记为可发布或可结算。
先看一个会失控的批量调用场景
假设一次批处理要同时读取库存、价格和优惠券。库存任务先成功,价格任务抛出异常,优惠券任务却还在等待网络返回。如果外层只捕获价格异常,最终很容易留下“库存已写入、价格未更新、优惠券稍后又回来覆盖状态”的半成品。
TaskGroup 的关键不是让请求更快,而是把这组任务的生命周期绑定在同一个上下文里。代码块退出前,组内任务必须结束;其中一个任务失败时,未完成的兄弟任务会收到取消信号。
TaskGroup 的最小可用写法
import asyncio
async def fetch_stock():
await asyncio.sleep(0.05)
return {"sku": "A-100", "stock": 8}
async def fetch_price():
await asyncio.sleep(0.02)
raise RuntimeError("price service unavailable")
async def fetch_coupon():
try:
await asyncio.sleep(0.30)
return {"discount": 10}
finally:
print("coupon task cleanup")
async def load_product():
async with asyncio.TaskGroup() as group:
stock_task = group.create_task(fetch_stock())
price_task = group.create_task(fetch_price())
coupon_task = group.create_task(fetch_coupon())
return stock_task.result(), price_task.result(), coupon_task.result()
async def main():
try:
result = await load_product()
print(result)
except* Exception as errors:
print("batch failed:", errors)
asyncio.run(main())
运行后,fetch_price() 的异常会让 fetch_coupon() 被取消,控制台仍会执行它的清理分支。load_product() 不会在组内任务尚未收口时提前返回,这就是它和“手动 create_task 后随便 gather”最重要的边界。

取消传播:为什么 finally 比 except 更可靠
兄弟任务收到的是 CancelledError。如果任务持有连接、临时文件或批次锁,清理动作应放在 finally 中;如果确实需要记录取消原因,可以先捕获,再继续抛出:
async def worker(resource):
try:
await resource.run()
except asyncio.CancelledError:
await resource.mark_cancelled()
raise
finally:
await resource.close()
这里的 raise 不能省略。吞掉取消异常会让 TaskGroup 误以为任务正常结束,调用方就可能把一个已经取消的批次当成成功。只有在非常明确的恢复场景下,才考虑 uncancel() 等更特殊的处理。
想保留部分结果,先区分“结果”与“成功状态”
TaskGroup 失败并不意味着所有已经完成的计算都不存在。可以让每个任务把自己的结果写入一个普通字典,但这个字典只能作为诊断或重试输入,不能直接代表批次成功。
async def run_batch():
results = {}
async def capture(name, operation):
try:
results[name] = await operation()
except asyncio.CancelledError:
results[name] = {"state": "cancelled"}
raise
except Exception as exc:
results[name] = {"state": "failed", "error": str(exc)}
raise
try:
async with asyncio.TaskGroup() as group:
group.create_task(capture("stock", fetch_stock()))
group.create_task(capture("price", fetch_price()))
group.create_task(capture("coupon", fetch_coupon()))
except* Exception as errors:
return {"state": "failed", "results": results, "errors": str(errors)}
return {"state": "ok", "results": results}
这个写法有两个收口点:每个任务负责留下可解释的局部状态,外层负责决定整批是 ok 还是 failed。不要因为字典里已经有库存结果,就把整个订单批次写成完成。

异常聚合与重试边界
TaskGroup 离开上下文时可能抛出 ExceptionGroup。使用 except* 可以按异常类型分拣,例如把网络错误放进一次批次重试,把数据校验错误直接送入人工处理。
try:
await run_batch()
except* TimeoutError as timeout_errors:
await schedule_retry("network", timeout_errors)
except* ValueError as data_errors:
await record_bad_input(data_errors)
重试也要有边界:只重试可恢复的下游错误,并携带批次 ID 做幂等;不要在 except* 中无条件再次创建同一组任务,否则一次故障会被放大成重试风暴。
三个常见误区
把 TaskGroup 当成 gather 的换名字
gather() 的异常与取消语义要结合参数理解,而 TaskGroup 更强调结构化并发的生命周期。选哪个取决于调用方是否需要“一组任务绑定在一起”的失败边界。
在取消分支里只打印日志
日志不能替代状态清理。连接、事务和临时资源需要明确关闭;处理完后继续抛出取消异常,才能让外层正确收口。
把部分结果直接当成功结果返回
部分结果适合诊断、补偿和重试。对外返回前应有一个明确的批次状态字段,否则调用方很难区分“只完成了一半”和“全部成功”。
相关问题
TaskGroup 从哪个 Python 版本开始可用?
它是 Python 3.11 标准库 asyncio 的能力。更老的运行时需要升级,或继续使用现有并发方案并自行约定取消和收口规则。
一个任务失败时,已经完成的任务会被撤销吗?
TaskGroup 不会自动撤销外部副作用。它只负责任务生命周期和异常传播;数据库写入、消息发送等操作仍要用事务、幂等键或补偿逻辑保护。
什么时候更适合使用 gather?
如果任务之间相互独立,调用方希望按返回顺序收集结果,且已有清晰的异常策略,gather() 仍然直接。需要绑定取消、清理和整体完成边界时,再优先考虑 TaskGroup。
把收口规则写进批处理协议
TaskGroup 真正解决的是边界:谁创建任务,谁等待任务,谁决定批次状态,谁负责清理。把这四件事固定下来,再把部分结果和失败原因作为独立字段保存,并发代码就不容易在异常路径上留下“看起来完成、实际上未完成”的状态。
Java 反射调用私有方法为什么失败:MethodHandles 查找模式与模块边界
- 上一篇
- Java 反射调用私有方法为什么失败:MethodHandles 查找模式与模块边界
- 下一篇
- Go map.clear 怎么处理临时缓存:复用容量、引用释放与基准核对
-
- 文章 · python教程 | 5小时前 | 日志 · 调试 · python · 多线程 · QueueListener Python QueueHandler 日志递归 队列日志
- Python logging.handlers.QueueHandler 为什么会递归:队列日志、线程边界与安全配置
- 306浏览 收藏
-
- 文章 · python教程 | 7小时前 | 数据校验 · Python教程 · dataclasses · 类型标注 · Python 数据类 dataclasses InitVar __post_init__
- Python dataclasses InitVar 如何把初始化参数传给 __post_init__:字段边界、校验顺序与序列化
- 147浏览 收藏
-
- 文章 · python教程 | 8小时前 | 日志 · 调试 · 异常处理 · python · 错误报告 异常链 Python traceback.TracebackException capture_locals
- Python traceback.TracebackException 怎么生成可控错误报告:异常链、局部变量与日志边界
- 179浏览 收藏
-
- 文章 · python教程 | 10小时前 | 缓存 · 性能优化 · python · Python 内存 lru_cache functools.cache
- Python functools.cache 与 lru_cache(maxsize=None) 的内存增长:命中率之外怎么做上限验收
- 391浏览 收藏
-
- 文章 · python教程 | 11小时前 | 日志 · python · 运维 · logging · RotatingFileHandler · 多进程日志 日志滚动 Python logging.handlers RotatingFileHandler 备份数量
- Python logging.handlers 如何按大小滚动日志:备份数量、编码设置与多进程边界
- 458浏览 收藏
-
- 文章 · python教程 | 14小时前 | SQLite · 数据恢复 · sqlite3 · Python教程 · 数据库备份 · Python SQLite 数据库备份 sqlite3 Connection.serialize Connection.deserialize
- Python sqlite3 Connection serialize 怎么导出数据库快照:备份窗口、内存占用与恢复校验
- 501浏览 收藏
-
- 文章 · python教程 | 17小时前 | 日志 · 标准库 · python · Python logging BufferingFormatter BufferingHandler MemoryHandler
- Python logging.BufferingFormatter 怎么批量组织日志:缓冲区、格式化与输出边界
- 481浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ljg-skills
- ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
- 5302次使用
-
- MELO音乐
- MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
- 4817次使用
-
- UniScribe
- UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
- 4757次使用
-
- 剧云
- 剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
- 5024次使用
-
- 万象有声
- 万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
- 4963次使用
-
- Python sqlite3 Connection serialize 怎么导出数据库快照:备份窗口、内存占用与恢复校验
- 2026-08-26 501浏览
-
- Python监控网页状态:requests异常处理实战
- 2026-05-29 501浏览
-
- TensorFlow模型部署为API的TF Serving方法
- 2026-05-26 501浏览
-
- Python字符串编码转换:encode与decode详解
- 2026-05-16 501浏览
-
- TensorFlow裁剪无用算子方法详解
- 2026-05-15 501浏览

