asyncio任务取消是协作式机制,task.cancel()仅设标记并抛CancelledError,需协程在await点响应;无await的CPU密集循环无法被取消,必须插入检查点或sleep(0)让出控制权。

asyncio 任务取消必须依赖协程内部的可中断点
直接调用 task.cancel() 不会强制终止协程执行,它只是设置取消标志并抛出 CancelledError —— 而这个异常只有在协程下一次 await 时才可能被触发。如果协程里写了个死循环:while True: do_something() 且中间没有 await,那 cancel() 就完全无效。
实操建议:
立即学习“Python免费学习笔记(深入)”;
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 所有长耗时逻辑必须拆解为带
await的小块,比如用await asyncio.sleep(0.1)插入检查点 - 在关键循环中显式检查
if asyncio.current_task().cancelled(): raise asyncio.CancelledError() - 避免在协程中调用阻塞式函数(如
time.sleep()、requests.get()),改用await asyncio.to_thread()或异步等价实现
使用 asyncio.shield() 保护关键清理步骤不被意外取消
用户点击“取消”后,你通常需要释放资源、关闭连接、回滚状态——这些清理动作本身不能被中断。但默认情况下,一旦父任务被取消,所有子 await 都会连锁抛出 CancelledError。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 把清理逻辑包进
asyncio.shield(),例如:await asyncio.shield(cleanup_db_connection()) - 不要对整个主任务加
shield,否则取消失效;只 shield 清理部分 -
shield()不能阻止取消信号到达,但能确保其内部协程运行完成(除非它自己也主动响应取消)
处理 CancelledError 时别吞掉异常或忽略 traceback
常见错误是写成 try: await long_job() except Exception: pass,这会吃掉 CancelledError,导致任务看似被取消,实则仍在后台跑,还可能引发资源泄漏。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 显式捕获
asyncio.CancelledError,并在处理后重新抛出(或用raise继续传播) - 若需记录日志,用
except asyncio.CancelledError as e:+logger.info("Task cancelled", exc_info=e) - 绝不要用空
except:或except BaseException:捕获,它们会拦截取消信号
在 HTTP 请求类场景中,用 aiohttp.ClientSession 配合 timeout 和 cancel 机制
比如用 aiohttp 下载大文件时,仅靠 task.cancel() 无法中断底层 TCP 连接,必须让客户端感知取消并主动断开。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 创建
ClientSession时传入timeout=aiohttp.ClientTimeout(total=None),避免超时干扰取消逻辑 - 发起请求时用
async with session.get(url) as resp:,其上下文管理器会在退出时自动关闭连接 - 在读取响应体循环中插入
await asyncio.sleep(0)或await resp.content.read(8192)(后者本身可取消) - 配合
asyncio.wait_for()可设硬性截止时间,但注意它内部也是靠 cancel 实现,仍需协程配合
cancel() 是“杀进程”,结果发现日志还在刷、CPU 还在跑、连接还挂着。

















