当前位置:首页 > 文章列表 > 文章 > python教程 > Python asyncio.TaskGroup 怎样在任务组结束时集中处理异常

Python asyncio.TaskGroup 怎样在任务组结束时集中处理异常

来源:17golang原创 2026-10-26 04:01:45 0浏览 收藏

将多个协程用TaskGroup.create_task加入任务组,在async with外层用except*捕获指定类型的异常子组。某个任务出现非取消异常后,TaskGroup会取消其余任务并等待它们结束,然后把已发生的非取消异常集中抛出;它不会为了收集所有业务错误而放任其余任务继续完成。

官方地址:https://www.python.org/

准备 Python 3.11 及以上环境

设想一个同时读取订单摘要和库存摘要的异步入口。如果由我维护这个入口,我会先明确:任何一项失败就让本次整体请求失败,还是允许各项独立失败。这里采用前一种约定,适合用TaskGroup。下面是为说明机制设计的本地模拟,未连接真实业务服务,也未在本机执行。

TaskGroup和except*从Python 3.11开始提供。保存示例为taskgroup_demo.py,使用Python 3.11及以上解释器,无需安装第三方库。不要把except*代码交给Python 3.10解析。

TaskGroup作用域与子任务归属结构说明图
结构说明图:任务归属于同一个TaskGroup,异常出口位于组作用域之外;不是运行截图。

把任务与异常出口放在同一个作用域

把try放在async with外面才能覆盖退出任务组时抛出的异常。示例让订单任务抛出ValueError,库存任务进入可取消的等待;finally代表资源清理的位置。

import asyncio

async def load_summary(name, delay, invalid=False):
    try:
        await asyncio.sleep(delay)  # 模拟可取消的异步等待
        if invalid:
            raise ValueError(f"{name}: 摘要格式错误")
        return {"name": name, "items": 3}
    finally:
        # 实际项目在此关闭本任务持有的资源。
        print(f"{name}: 清理结束")

def leaves(group):
    # 子组可以继续嵌套,递归取得叶子异常。
    for error in group.exceptions:
        if isinstance(error, BaseExceptionGroup):
            yield from leaves(error)
        else:
            yield error

async def main():
    tasks = {}
    try:
        async with asyncio.TaskGroup() as group:
            tasks["订单"] = group.create_task(
                load_summary("订单", 0.02, invalid=True), name="orders"
            )
            tasks["库存"] = group.create_task(
                load_summary("库存", 1.0), name="inventory"
            )
    except* ValueError as errors:
        for error in leaves(errors):
            print(f"输入问题: {error}")
    else:
        # 仅在任务组正常完成时读取全部结果。
        print({key: task.result() for key, task in tasks.items()})

if __name__ == "__main__":
    asyncio.run(main())

except* ValueError匹配异常组中的ValueError叶子,并保留子组结构,所以errors.exceptions不保证是扁平列表。辅助函数leaves只做遍历,便于逐条记录。本例没有用except* Exception吞掉所有未知问题。

任务组正常完成后才能直接按成功路径读取结果;若已经处理过异常,不能随后无条件对全部任务调用result(),因为其中可能有失败或被取消的任务。

运行时观察取消与异常分组

读者可运行以下命令:

python3 taskgroup_demo.py

预期会看到任务清理消息和“输入问题”消息,而不会打印整批成功结果。输出行的具体顺序与调度有关,不应据此推导任务执行顺序;这里提供的是阅读判断标准,没有提供伪造的实测输出。

发生的情况组退出时的含义
所有任务成功正常离开async with,可读取结果
一个任务抛ValueError其余未完成任务被取消,等待退出后抛异常组
多个任务在取消生效前已经失败已发生的非取消异常可共同进入异常组
任务因组内取消而结束CancelledError通常不作为普通失败加入异常组

集中处理并不保证收集到每个任务原本可能产生的错误。库存任务可能在到达自己的业务检查之前就已取消。若需求是每项都独立完成并形成逐项结果,应在任务内部把预期业务失败转换为明确结果对象,而不是误用TaskGroup的失败即取消语义。

异常组与类型子组静态结构说明图
结构说明图:异常组可包含不同类型及嵌套子组,except*按叶子异常类型筛选;不表示每次都会出现多种失败。

扩展实验:保留未知异常和部分结果

可以把订单任务的ValueError改成RuntimeError,理解未知异常会继续向上传播。若组内同时存在ValueError与其他未匹配异常,except*只处理匹配部分,剩余部分仍会抛出。不要用裸except把外层请求伪装成成功。

另一个实验是给多个任务设置相近延迟并让它们分别失败。实际能收集多少个异常取决于失败与取消的时机,不能依赖固定数量或固定排序,也不应根据exceptions列表的位置反推任务身份。业务异常消息、附加字段或任务侧日志应携带自己的业务标识。

如果确实要保留部分成功结果,在处理完异常之后逐个判断task.cancelled(),再检查task.exception(),最后才读取task.result()。此时TaskGroup已经等待组内任务结束,判断应发生在组退出之后。部分成功如何对外返回仍是业务接口约定,不能靠捕获异常自动决定。

TaskGroup只管理通过该组create_task创建的任务。若子协程另用asyncio.create_task生成游离任务,不能假设它也会被当前组完整管理。

退出任务组后的清理与常见问题

资源释放放进finally或异步上下文管理器。若显式捕获asyncio.CancelledError做清理,通常应在清理后重新raise;吞掉取消可能干扰TaskGroup的结构化并发行为。CancelledError继承BaseException,普通except Exception不会捕获它。

为什么只有一个失败也会进入except*?

TaskGroup退出时可以把单个普通失败包装成异常组;except*面对的不是“必须至少两个错误”的特殊情况。

能否在同一个try中同时写except和except*?

不能。同一try的处理分支不能混用两种语法,需要嵌套try安排不同边界。except*分支内部也不能使用return、break或continue。

KeyboardInterrupt也会变成普通异常组吗?

KeyboardInterrupt和SystemExit有特殊规则:任务组会取消并等待其他任务,然后重新抛出原始异常。外部取消也不是应当随手忽略的业务失败。

async with内部主体抛错怎么办?

组主体的非取消异常也会触发取消与等待,并可能与任务异常一起被分组。把捕获边界放在整个async with外侧可以覆盖这一出口。

本文只用本地协程与标准库,实验结束后无需清理外部服务。真正接入网络客户端时,每个客户端、连接或事务仍应有明确的释放与失败语义。

官方资料:TaskGroup任务与异常契约、异常组与except*说明。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Java CRC32C 怎样计算分块数据的校验值Java CRC32C 怎样计算分块数据的校验值
上一篇
Java CRC32C 怎样计算分块数据的校验值
Java 模块系统怎样导出只读 API 包
下一篇
Java 模块系统怎样导出只读 API 包
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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模型性能。
    697次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    707次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    777次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    738次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    571次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码