当前位置:首页 > 文章列表 > 文章 > python教程 > Python asyncio.TaskGroup怎样让一组协程一起失败并统一收集异常怎样完成一个可复用的最小示例

Python asyncio.TaskGroup怎样让一组协程一起失败并统一收集异常怎样完成一个可复用的最小示例

来源:17golang原创 2026-11-10 04:56:10 0浏览 收藏

用 asyncio.TaskGroup 管理同一业务操作的多个协程:子任务出现非取消异常时,任务组会请求取消尚未完成的同组任务,等待它们结束,再向外抛出异常组。调用方可在 async with 外用 except* 按类型处理错误。“一起失败”是统一结束和传播的约定,不意味着每个任务同时抛错,也不会回滚已经发生的副作用。

TaskGroup 从 Python 3.11 提供,本文示例只使用 3.11 已有能力,不依赖后续版本新增接口。官方文档:https://docs.python.org/3/library/asyncio-task.html。

1. 把同一业务操作的任务放入共同作用域

把一次聚合查询需要的 profile、stock 和 slow 三项操作看作一个整体:只要缺少必要结果,就不交付完整聚合对象。父协程拥有 TaskGroup,任务字典保留具名任务,子任务的生命周期由共同作用域管理。这是结构化并发在业务接口上的一种具体用法。

父协程与任务组的所有权
父协程与任务组的所有权;静态关系说明图,非截图或运行证据。

如果每一项都是独立下载、允许分别成功失败,就不一定适合遇错取消其余任务;可以选择不同的结果收集策略。关键是先定义业务上的共同成败,而不是看到并发就统一套 TaskGroup。

2. 用最小示例观察失败与清理

保存下面代码为 task_bundle.py。它让 stock 报错,slow 在可取消等待中接受取消,finally 负责演示清理。本文未执行程序,所述现象是代码语义说明。

import asyncio


async def load(name, delay, fail=False):
    try:
        # sleep 模拟可取消的异步等待,不执行真实网络请求
        await asyncio.sleep(delay)
        if fail:
            raise ValueError(f"{name}: 数据不可用")
        return f"{name}:ok"
    finally:
        # 真实客户端应在此释放本任务持有的资源
        print(f"cleanup:{name}")


async def run_bundle():
    tasks = {}
    async with asyncio.TaskGroup() as group:
        tasks["profile"] = group.create_task(load("profile", 0.01))
        tasks["stock"] = group.create_task(load("stock", 0.02, True))
        tasks["slow"] = group.create_task(load("slow", 10))
    # 仅在整个任务组成功退出时读取全部结果
    return {name: task.result() for name, task in tasks.items()}


def leaves(error):
    # 递归展开嵌套结构,保留每个叶子异常的类型
    if isinstance(error, BaseExceptionGroup):
        for child in error.exceptions:
            yield from leaves(child)
    else:
        yield error


async def main():
    try:
        result = await run_bundle()
        print(result)
    except* ValueError as errors:
        # 只消费已明确支持处理的业务错误子组
        for error in leaves(errors):
            print(type(error).__name__, str(error))


if __name__ == "__main__":
    asyncio.run(main())
# 使用 Python 3.11 或以上解释器执行独立示例
python3 task_bundle.py

按示例逻辑,stock 的 ValueError 会离开任务组;slow 不必等待完整十秒即可响应取消并执行清理。profile 可能已经成功,它不需要变成失败。输出中的清理先后顺序不应作为程序契约。任务组异常退出时,不会走到汇总 task.result() 的那一行。

取消是协作式请求:协程必须有机会运行到可响应取消的位置。CPU 密集死循环、同步阻塞库或吞掉 CancelledError 的代码,都可能延迟作用域退出。finally 中若执行异步清理,应遵循对应资源库的取消安全约定;清理代码自己抛错也可能加入失败信息。

3. 用 except* 保留异常的类型结构

普通业务异常通常组成 ExceptionGroup;其叶子可能包括 ValueError、RuntimeError 等类型,except* 处理器接收匹配的子组。CancelledError 用于协作取消,finally 清理与它相关,但普通子任务取消不会作为同类业务错误叶子收集进上述异常组。

异常组与类型处理器关系
异常组与类型处理器关系;静态关系说明图,非截图或运行证据。

示例 leaves 递归访问 BaseExceptionGroup.exceptions,适用于任务组内部还有任务组的场景。不要假设 exceptions 第一层永远都是单个业务异常。except* ValueError 仅处理匹配部分,未匹配的 RuntimeError 等异常仍然向外传播;这比捕获后只打印“任务失败”更能保留定位线索。

需求处理方式
只处理输入类失败except* ValueError,其他类型继续传播
统一记录全部普通业务错误边界层记录异常组,并保留 traceback 与任务标识
保留外部取消不要吞掉 CancelledError,清理后继续传播
逐项容错并返回全部结果重新选择结果策略,避免误用共同失败语义

KeyboardInterrupt 和 SystemExit 有特殊传播规则,不能把它们当作普通业务错误汇总处理。except* 也不能与普通 except 混放在同一个 try 的处理器列表中;需要时增加内外两层 try。

4. 把任务组接入实际调用边界

实际接口应把客户端连接放在合适的外部资源作用域中,确保任务组先结束,再关闭共享客户端。子任务自身持有的响应体、游标等资源仍由子任务释放。TaskGroup 不会自动替每个库完成资源清理。

任务组本身不提供并发数量上限,也不让同步函数自动变为异步。大批量输入应分批或用 Semaphore 约束真实下游并发;整组期限可结合 asyncio.timeout,子调用还应配置客户端期限。取消线程适配层的等待,也不等于强行终止其底层工作线程。

如果对子任务异常先捕获并返回备用值,任务组会将该任务视为正常完成。只有业务明确允许降级时才这样写,并把降级状态纳入结果;否则会掩盖本应触发整组失败的信号。

为什么只收集到一个错误?

第一个错误出现后,其余任务可能及时取消而未产生自身业务异常。任务组汇总实际发生的错误,不保证每个任务都报告一个失败。

可以在块内直接调用 asyncio.create_task 吗?

这样创建的任务不会自动成为该 TaskGroup 的成员。希望受同组管理时,使用 group.create_task。

TaskGroup 与 gather 的区别是什么?

gather 默认传播首个异常时,不会因此自动取消所有其他 awaitable;TaskGroup 更适合共同生命周期和失败时取消同组任务的场景。选择取决于业务失败策略。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
JVM Class Data Sharing怎样生成类数据归档缩短启动时间怎样在批处理代码中减少额外分配JVM Class Data Sharing怎样生成类数据归档缩短启动时间怎样在批处理代码中减少额外分配
上一篇
JVM Class Data Sharing怎样生成类数据归档缩短启动时间怎样在批处理代码中减少额外分配
Python asyncio.TaskGroup怎样让一组协程一起失败并统一收集异常为什么结果为空或不符合预期
下一篇
Python asyncio.TaskGroup怎样让一组协程一起失败并统一收集异常为什么结果为空或不符合预期
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    965次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    966次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1077次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1057次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    888次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码