踩坑了!Spring Boot 异步调用 CompletableFuture 的坑你踩过几个

核心内容摘要

AutoDock-Vina分子对接实用指南:从入门到实践
Linux下ibus输入法词库自定义与优化指南

机器学习入门(二十)支持向量机SVM

Python 异步编程实战:掌握任务取消的艺术与优雅退出策略引言:当"停下来"比"跑起来"更难在我职业生涯的第三年,我负责的一个数据采集系统出现了严重的资源泄漏问题。

每当用户点击"停止"按钮,系统表面上停止了,但后台仍有数十个网络连接保持活跃,数据库事务未提交,临时文件散落一地。

这次惨痛的经历让我意识到:如何优雅地停止一个异步任务,远比启动它更具挑战性。

在异步编程的世界里,任务取消(Task Cancellation)是一门被严重低估的艺术。

大多数开发者将 90% 的精力放在如何让任务高效运行,却忽略了那关键的 10%——如何让它们安全、干净、彻底地停下来。

今天,我将通过实战案例和深度剖析,带你全面掌握 Python asyncio 中任务取消的精髓。

任务取消的本质:协作式而非强制式

1 理解 asyncio 的取消机制与线程的强制终止不同,asyncio 的任务取消是协作式的:importasyncioasyncdefnaive_task():"""天真的任务:不处理取消"""print("任务开始")try:# 长时间运行的操作foriinrange(

:print(f"执行步骤{i}")awaitasyncio.sleep(

print("任务完成")exceptExceptionase:print(f"捕获异常:{e}")asyncdeftest_naive_cancellation():task=asyncio.create_task(naive_task())# 等待 3 秒后取消awaitasyncio.sleep(

print("\n⚠️ 尝试取消任务...")task.cancel()try:awaittaskexceptasyncio.CancelledError:print("✅ 任务已被取消")# asyncio.run(test_naive_cancellation())关键发现:cancel()方法只是设置一个标志,并不立即停止任务下一次await时会抛出CancelledError异常如果任务中没有await点,取消将无法生效

2 取消的三个阶段importasyncioimporttimeasyncdefthree_phase_task():"""展示取消的三个阶段"""print("阶段1:任务正常运行")try:awaitasyncio.sleep(

print("阶段2:继续运行(如果未被取消)")awaitasyncio.sleep(

exceptasyncio.CancelledError:print("阶段3:取消信号已接收")# 清理工作print(" - 关闭数据库连接")print(" - 保存中间状态")print(" - 释放文件句柄")raise# 重要:重新抛出 CancelledErrorfinally:print("阶段4:finally 块总会执行")print(" - 执行最终清理")asyncdefdemo_three_phases():task=asyncio.create_task(three_phase_task())awaitasyncio.sleep(

task.cancel()try:awaittaskexceptasyncio.CancelledError:print("\n主程序:确认任务已取消")asyncio.run(demo_three_phases())输出解析:阶段1:任务正常运行 阶段3:取消信号已接收 - 关闭数据库连接 - 保存中间状态 - 释放文件句柄 阶段4:finally 块总会执行 - 执行最终清理 主程序:确认任务已取消

边界情况处理:魔鬼在细节中

1 边界情况一:屏蔽取消信号(反模式)asyncdefcancel_resistant_task():"""❌ 错误示范:吞掉 CancelledError"""try:whileTrue:print("我停不下来!

")awaitasyncio.sleep(

exceptasyncio.CancelledError:print("收到取消信号,但我选择无视...")# 危险:不重新抛出异常awaitasyncio.sleep(

# 继续运行print("哈哈,我还活着")asyncdefdemo_cancel_resistance():task=asyncio.create_task(cancel_resistant_task())awaitasyncio.sleep(

task.cancel()print("已发送取消信号")try:awaitasyncio.wait_for(task,timeout=

exceptasyncio.TimeoutError:print("⚠️ 任务拒绝取消,超时强制退出")exceptasyncio.CancelledError:print("任务已取消")# asyncio.run(demo_cancel_resistance())正确做法:asyncdefwell_behaved_task():"""✅ 正确示范:响应取消但完成必要清理"""try:whileTrue:print("执行任务...")awaitasyncio.sleep(

exceptasyncio.CancelledError:print("收到取消信号,执行清理...")# 执行必要的同步清理(注意:不要有 await)print("清理完成")raise# 关键:必须重新抛出

2 边界情况二:嵌套任务的级联取消importasyncioasyncdefchild_task(task_id,duration):"""子任务"""try:print(f" 子任务{task_id}开始")awaitasyncio.sleep(duration)print(f" 子任务{task_id}完成")returnf"Result-{task_id}"exceptasyncio.CancelledError:print(f" 子任务{task_id}被取消")raiseasyncdefparent_task():"""父任务:管理多个子任务"""

17c·MOC登录不了怎么办-17c·MOC登录不了怎么办应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123