Python 异步任务取消与异常处理

发布时间 - 2026-01-28 00:00:00    点击率:
asyncio.cancel() 不能强制终止协程,仅设取消标记并在下次 await 时抛 CancelledError;需协程主动配合(如插入 await、检查 cancelled())才能真正响应取消。

asyncio.cancel() 能否真正终止正在运行的协程

asyncio.cancel() 并不会强制中断协程执行,它只是给 Task 设置一个取消标记,并在下一次 await 时抛出 CancelledError。如果协程里全是 CPU 密集型计算、没任何 await,那取消就完全不生效。

常见错误现象:调用 task.cancel() 后,任务仍在后台跑满 CPU,日志里也看不到异常。

  • 必须确保协程中存在可取消的挂起点,比如 await asyncio.sleep(1)await aiohttp.get(...)await queue.get()
  • 长时间计算逻辑要主动插入 await asyncio.sleep(0)await asyncio.shield(...)(慎用)来让出控制权
  • 捕获 CancelledError 后,通常应直接返回或清理资源,不要“吞掉”后继续执行

如何安全地在 cancel 后释放资源(如文件句柄、连接)

协程被取消时,CancelledError 是继承自 BaseException 的,所以普通 except Exception: 捕获不到,必须显式处理。

使用场景:异步写文件、维持 WebSocket 连接、持有数据库连接池租约等。

  • try/except CancelledError: 包裹关键清理逻辑,或更推荐用 async with + 支持异步 __aexit__ 的上下文管理器
  • 避免在 finally 块里做耗时 await 操作(如 await db.close()),因为此时事件循环可能已关闭;可改用 loop.create_task() 延迟调度
  • 若清理本身也可能被取消(比如 await redis.connection_pool.disconnect() 超时),需加超时控制:await asyncio.wait_for(cleanup(), timeout=2.0)

asyncio.gather() 中部分任务被取消时的异常传播行为

asyncio.gather() 默认遇到任意子任务异常(包括 CancelledError)就立即停止并抛出,但具体抛什么,取决于 return_exceptions 参数。

参数差异:

  • return_exceptions=False(默认):只要有一个任务被取消,整个 gather 就 raise CancelledError,其余任务状态不确定(可能还在跑)
  • return_exceptions=True:被取消的任务返回 CancelledError 实例而非抛出,其他任务继续运行,最终结果是混合列表
  • 注意:即使设了 return_exceptions=True,主协程仍可能因父级取消而中断,不能依赖它“保底执行完”

示例:results = await asyncio.gather(task_a, task_b, return_exceptions=True) —— 若 task_a 被取消,results[0]CancelledError 实例,results[1

]task_b 的返回值。

取消信号从上层传入深层协程的常用模式

深层协程(比如嵌套三层 await)无法自动感知外层 Task 是否被取消,必须显式传递取消上下文或检查 asyncio.current_task().cancelled()

容易踩的坑:写了个工具函数 fetch_with_retry(),内部重试逻辑没检查取消状态,导致即使外层已 cancel,它还在傻等重试间隔。

  • 推荐方式:把 asyncio.Taskasyncio.CancelScope(来自 anyio)作为参数传入,或使用 asyncio.shield() 显式保护不可取消段
  • 轻量检查:在循环或重试开头加 if asyncio.current_task().cancelled(): raise asyncio.CancelledError()
  • 避免用 time.sleep()while True: 死循环,它们不响应取消;改用 await asyncio.sleep() 并配合取消检查

复杂点在于,取消不是“硬杀”,而是协作式中断 —— 每一层都要愿意停下来,否则信号就断在半路了。


# python  # redis  # websocket  # 工具  # ai  # 异步任务  # red  # if  # while  # try  # 循环  # 继承  # raise  # finally  # 事件  # 异步  # 数据库  # 抛出  # 还在  # 重试  # 并在  # 都要  # 句柄  # 长时间  # 下一  # 写了  # 管理器 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: Laravel如何实现多语言支持_Laravel本地化与国际化(i18n)配置教程  Laravel如何使用Socialite实现第三方登录?(微信/GitHub示例)  公司门户网站制作公司有哪些,怎样使用wordpress制作一个企业网站?  如何快速重置建站主机并恢复默认配置?  Laravel怎么进行数据库事务处理_Laravel DB Facade事务操作确保数据一致性  php结合redis实现高并发下的抢购、秒杀功能的实例  进行网站优化必须要坚持的四大原则  Laravel如何实现RSS订阅源功能_Laravel动态生成网站XML格式订阅内容【教程】  Laravel如何处理文件上传_Laravel Storage门面实现文件存储与管理  Laravel API资源类怎么用_Laravel API Resource数据转换  Laravel如何获取当前登录用户信息_Laravel Auth门面使用与Session用户读取【技巧】  韩国服务器如何优化跨境访问实现高效连接?  网站建设整体流程解析,建站其实很容易!  深入理解Android中的xmlns:tools属性  如何在IIS中新建站点并解决端口绑定冲突?  Laravel怎么做缓存_Laravel Cache系统提升应用速度的策略与技巧  html5怎么画眼睛_HT5用Canvas或SVG画眼球瞳孔加JS控制动态【绘制】  如何基于云服务器快速搭建个人网站?  香港服务器网站生成指南:免费资源整合与高速稳定配置方案  php在windows下怎么调试_phpwindows环境调试操作说明【操作】  如何在橙子建站上传落地页?操作指南详解  小米17系列还有一款新机?主打6.9英寸大直屏和旗舰级影像  Android okhttputils现在进度显示实例代码  JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)  JavaScript Ajax实现异步通信  Laravel如何优雅地处理服务层_在Laravel中使用Service层和Repository层  如何在宝塔面板中修改默认建站目录?  Laravel如何处理跨站请求伪造(CSRF)保护_Laravel表单安全机制与令牌校验  如何基于PHP生成高效IDC网络公司建站源码?  jQuery validate插件功能与用法详解  标准网站视频模板制作软件,现在有哪个网站的视频编辑素材最齐全的,背景音乐、音效等?  详解Huffman编码算法之Java实现  手机网站制作与建设方案,手机网站如何建设?  武汉网站设计制作公司,武汉有哪些比较大的同城网站或论坛,就是里面都是武汉人的?  Python高阶函数应用_函数作为参数说明【指导】  百度浏览器如何管理插件 百度浏览器插件管理方法  如何在HTML表单中获取用户输入并用JavaScript动态控制复利计算循环  邀请函制作网站有哪些,有没有做年会邀请函的网站啊?在线制作,模板很多的那种?  如何有效防御Web建站篡改攻击?  简单实现jsp分页  lovemo网页版地址 lovemo官网手机登录  javascript中对象的定义、使用以及对象和原型链操作小结  深圳网站制作设计招聘,关于服装设计的流行趋势,哪里的资料比较全面?  如何为不同团队 ID 动态生成多个非值班状态按钮  如何正确下载安装西数主机建站助手?  东莞专业网站制作公司有哪些,东莞招聘网站哪个好?  黑客入侵网站服务器的常见手法有哪些?  如何在阿里云虚拟主机上快速搭建个人网站?  如何挑选高效建站主机与优质域名?  Python自动化办公教程_ExcelWordPDF批量处理案例