asyncio 如何在 gather 中统一处理所有 CancelledError

默认情况下asyncio.gather()遇子任务取消会直接抛出CancelledError并中断执行;启用return_exceptions=True后,CancelledError作为结果项返回,需用isinstance(res, asyncio.CancelledError)显式判断。

asyncio 如何在 gather 中统一处理所有 cancellederror

gather 中的 CancelledError 默认不传播

默认情况下,asyncio.gather() 遇到某个子任务被取消时,会把 CancelledError 包装进返回的异常列表(如果用了 return_exceptions=True),否则直接抛出——但这个抛出行为是“中断整个 gather”,其余未完成任务会被一并取消,且你拿不到它们的原始状态或错误。这不是“统一处理”,而是被动崩溃。

用 return_exceptions=True + 显式检查每个结果

真正可控的方式是启用 return_exceptions=True,让所有异常(包括 CancelledError)都作为结果项返回,再遍历判断:

import asyncio

async def task_a(): await asyncio.sleep(1) return "a"

async def task_b(): raise asyncio.CancelledError() # 模拟被 cancel

async def task_c(): await asyncio.sleep(0.5) return "c"

async def main(): tasks = [task_a(), task_b(), task_c()] results = await asyncio.gather(*tasks, return_exceptions=True)

for i, res in enumerate(results):
    if isinstance(res, asyncio.CancelledError):
        print(f"task {i} was cancelled")
    elif isinstance(res, Exception):
        print(f"task {i} raised {type(res).__name__}: {res}")
    else:
        print(f"task {i} succeeded: {res}")

asyncio.run(main())

  • return_exceptions=True 是前提,否则 CancelledError 会立刻中断执行
  • 必须用 isinstance(res, asyncio.CancelledError) 判断,不能只靠 except CancelledError —— 因为它已不是被抛出,而是被“返回”
  • 注意:即使某 task 已被 cancel,它仍可能在取消前完成了部分逻辑(比如刚写完日志),所以 CancelledError 出现在结果里,不代表它完全没执行

想彻底屏蔽 CancelledError?别直接吞掉

有时你想忽略取消、只关心成功值。但直接在每个协程里 try/except CancelledError: pass 是危险的——这会让协程无法响应上层取消信号,破坏 asyncio 的协作取消机制。

易森网络企业版

易森网络企业版

如果您是新用户,请直接将本程序的所有文件上传在任一文件夹下,Rewrite 目录下放置了伪静态规则和筛选器,可将规则添加进IIS,即可正常使用,不用进行任何设置;(可修改图片等)默认的管理员用户名、密码和验证码都是:yeesen系统默认关闭,请上传后登陆后台点击“核心管理”里操作如下:进入“配置管理”中的&ld

下载

  • 正确做法是:让子协程保持可取消性,只在 gather 结果层过滤
  • 例如提取所有非异常结果:[r for r in results if not isinstance(r, BaseException)]
  • 若需区分“主动取消”和“其他异常”,保留 CancelledError 单独处理,不要和 ValueError 等混在一起 except Exception

超时场景下 CancelledError 最容易被误判

asyncio.wait_for() 包裹单个 task 后传给 gather,超时触发的取消会产生 TimeoutError,但被 wait_for 内部转成了 CancelledError 抛给子协程。此时你在 gather 结果里看到的 CancelledError,其实源头是超时,不是手动调用 task.cancel()

  • 这种间接取消无法从异常类型本身分辨,需要结合上下文(比如是否用了 wait_for
  • 若需统一标记“超时导致的取消”,建议在外层封装一层自定义异常,而不是依赖 CancelledError 的原始形态
  • asyncio.shield() 不能防止 wait_for 的取消,它只防外部直接 cancel;这点常被忽略

实际中,最易被跳过的点是:以为 return_exceptions=True 后就能用 except CancelledError 捕获——其实不行,它已经变成普通返回值了。

https://www.php.cn/faq/2038101.html

发表回复

Your email address will not be published. Required fields are marked *