Python 3.11+ 并发应优先用 asyncio.TaskGroup 而非 gather,因其提供结构化并发:强制 async with 边界、自动资源清理、协作取消、ExceptionGroup 异常聚合及动态任务支持。

直接说结论:Python 3.11+ 的并发任务,只要涉及错误传播、资源清理或动态增删子任务,就该用 asyncio.TaskGroup,而不是 asyncio.gather。这不是“升级替代”,而是语义根本不同——TaskGroup 是结构化并发的强制边界,gather 只是结果聚合工具。
TaskGroup 必须用 async with,否则 RuntimeErrors 立刻报
TaskGroup 不是普通类,不能 tg = asyncio.TaskGroup(),也不能手动调用 __aenter__。它只接受一种写法:async with asyncio.TaskGroup() as tg:。一旦漏掉 async with,运行时会抛 RuntimeError: TaskGroup is closed 或 no active task group。
- 所有子任务必须在
async with块内通过tg.create_task()启动;用asyncio.create_task()启动的任务不会被纳入管理 - 块退出时(无论正常结束还是异常中断),框架自动等待所有已启动任务完成或取消,并执行其
async with/finally清理逻辑 - 不能在块外
await某个子任务,否则破坏生命周期约束,可能引发未定义行为
异常处理必须用 except*,老式 except 会完全失效
TaskGroup 抛出的不是单个异常,而是 ExceptionGroup(或 BaseExceptionGroup)。如果你还写 except ValueError:,这个 except 根本捕获不到任何东西——因为 ValueError 被包在 ExceptionGroup 里了。
- 正确写法是
except* ValueError as eg:,其中eg.exceptions是一个列表,包含所有匹配的子异常 - 可以多层
except*并列,比如同时写except* httpx.HTTPStatusError:和except* asyncio.TimeoutError: - 如果想兜底捕获所有异常,用
except* BaseException:,但注意这也会捕获KeyboardInterrupt等
TaskGroup 会协作式取消其余任务,gather 默认不会
这是最常踩坑的点:用 gather() 时,一个子任务抛 ValueError,其他任务照常跑,哪怕它们正在发 HTTP 请求、写文件、持锁。而 TaskGroup 在第一个异常发生时,立刻向其余任务发送 CancelledError,并等待它们自行退出(包括执行 async with 的 __aexit__)。
立即学习“Python免费学习笔记(深入)”;
- 常见错误现象:日志只看到第一个异常,但数据库连接没关、HTTP 连接池爆满、临时文件残留
-
gather(return_exceptions=True)把异常塞进返回值列表,你得手动遍历每个元素判断类型;TaskGroup不需要你做这事,异常统一聚合后一次抛出 - 若需部分失败继续执行(比如“最多容忍 2 个失败”),
TaskGroup本身不支持,但你可以用except*捕获后忽略某些异常,让上下文继续退出
结果顺序不保证,别依赖索引取值
gather() 返回结果顺序严格对应传入 awaitable 的顺序;TaskGroup 不承诺顺序,也不提供索引映射。如果你需要知道哪个结果对应哪个输入,必须让子任务自己返回标识信息。
- 错误写法:
results = [task1.result(), task2.result(), task3.result()]—— 顺序不确定,且task1.result()可能抛InvalidStateError(如果还没完成) - 推荐做法:子任务返回元组,如
return (user_id, data),或用字典收集:results[user_id] = await task.result() - 动态任务场景(比如根据响应决定是否拉下一页),
TaskGroup支持在async with块内随时调用tg.create_task();gather要求任务列表必须预先固定
真正难的不是语法,而是把“任务必须成组生灭”这个约束刻进习惯——比如忘记在子任务里加 async with 管理连接,或者在 except* 里漏掉某类业务异常,都会让取消和清理失效。结构化并发不是加个 async with 就完事,它是整个异步逻辑的骨架约束。


















