asyncio.TaskGroup 是用于结构化并发的强制性边界,防止任务泄露、确保异常不静默、统一管理子任务生命周期;必须用 async with 进入,自动等待或取消全部子任务,不可手动实例化或外泄引用。

Python 3.11 的 asyncio.TaskGroup 不是“更好用的 gather”,它是用来防止任务泄露、确保异常不静默、让并发行为可预测的结构化边界。没它,asyncio.create_task() 像撒手没缰绳的马;有它,所有子任务才真正在一个“可控牢笼”里生灭。
必须用 async with,否则直接报错
这是硬性语法约束,不是风格建议。直接实例化 asyncio.TaskGroup() 或试图手动调用 __aenter__ 都会触发 RuntimeError: no active task group。
-
async with asyncio.TaskGroup() as tg:是唯一合法入口,离开该块时自动等待全部子任务结束(或被取消) - 不能在
async with外保留对tg的引用,更不能把它存进全局变量或类属性里传出去 - 如果想提前退出(比如某个条件满足后不再启新任务),直接
return或break即可 ——async with退出时仍会收尾
tg.create_task() 和 asyncio.create_task() 的本质区别
前者是后者的“受管版本”:它把任务生命周期完全交由 TaskGroup 控制,后者则完全由你手动管理,极易遗漏。
-
tg.create_task()返回的Task对象,其取消行为受组内统一调度 —— 任一失败,其余全 cancel -
asyncio.create_task()创建的任务一旦脱离引用(比如循环中反复赋值覆盖变量),可能被静默丢弃,变成“僵尸任务” -
tg.create_task()可以在async with块内任意位置动态调用,包括在另一个子任务的await后、if分支里、甚至响应 API 数据后决定是否拉下一页
异常传播规则:只抛第一个,但会取消全部
这和 asyncio.gather(return_exceptions=False) 表面相似,但机制完全不同:前者是“等所有完成再汇总异常”,后者是“一个出事,立刻熔断”。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 如果三个任务同时跑,第二个抛了
ValueError,TaskGroup会立即向其余两个发送 cancel 信号,并在async with退出时只抛出那个ValueError - 不会打包成
ExceptionGroup,除非**多个任务在同一时刻抛出不同异常**(比如网络超时 + JSON 解析失败同时发生) - 想捕获多异常?得用
except*语法,而不是普通except
别拿它当 gather 的平替:参数、返回值、适用场景都不同
TaskGroup 不接受协程对象列表,也不返回结果列表 —— 它只负责生命周期管理。你要自己存 Task 引用,再调用 .result() 拿结果。
-
asyncio.gather(coro1(), coro2())→ 直接返回[res1, res2];tg.create_task(coro1())→ 得你自己存变量,最后task1.result() -
gather支持return_exceptions=True把异常也当结果返回;TaskGroup不支持这个开关,异常就是异常 - 需要“部分失败继续执行”?
TaskGroup不干这事 —— 那是asyncio.wait(..., return_when=asyncio.FIRST_COMPLETED)或手动create_task+done/pending拆解的活
真正容易被忽略的点是:TaskGroup 的“结构化”不只体现在语法上,而在于它强制你把并发边界显式划出来。一旦你习惯在每个业务逻辑块里都用 async with asyncio.TaskGroup() 包一层,你就不会再写出“启动一堆任务然后忘了等”的代码 —— 因为语法根本不让你这么写。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










