Python协程取消是协作式机制,需主动检查CancelledError并配合可取消await点;纯CPU密集型代码无法取消,须插入挂起点;清理逻辑在finally中仍会执行;子任务不自动继承取消,需手动管理。

Python 协程的取消机制不是自动传播的,必须主动检查、响应并协作完成;它依赖 asyncio.Task 的 cancel() 方法触发,但真正中断执行靠的是协程内部对 CancelledError 的捕获与处理,以及可取消等待对象(如 asyncio.sleep()、await task)在被取消时及时抛出异常。
取消的本质是协作式中断
协程不会被“强行杀死”,取消请求只是设置任务状态为 CANCELLED,并调度一次事件循环——下一次遇到可取消的 await 点时,运行时才检查是否被取消,并抛出 asyncio.CancelledError(继承自 Exception,但不继承自 BaseException)。这意味着:
- 纯 CPU 密集型协程(如
while True: pass)无法被取消,必须插入await asyncio.sleep(0)或其他挂起点让出控制权 -
try/except Exception:不会捕获CancelledError,需显式写except asyncio.CancelledError:或except BaseException: - 若在
finally或async with/async for清理逻辑中被取消,仍会进入清理块,适合释放资源
正确响应取消的常见模式
编写健壮协程时,应预设取消可能发生在任意 await 处。推荐做法包括:
- 在关键清理位置用
try/finally或async with保证资源释放(例如关闭连接、删除临时文件) - 长时间运行的循环内定期
await asyncio.sleep(0)或await asyncio.wait([pending_task], timeout=0.1)提供取消检查点 - 使用
asyncio.shield()包裹不希望被取消的子任务(如心跳发送),避免父任务取消时误伤 - 调用外部库异步方法前,确认其是否支持取消(如
aiohttp.ClientSession.get()可被取消,而某些自定义 Future 可能不响应)
取消传播与父子任务关系
默认情况下,子任务(如通过 asyncio.create_task() 启动)不会自动随父任务取消而取消;取消是单向、非继承的。若需级联取消,需手动管理:
Android应用程序是通过消息来驱动的,系统为每一个应用程序维护一个消息队例,应用程序的主线程不断地从这个消息队例中获取消息(Looper),然后对这些消息进行处理(Handler),这样就实现了通过消息来驱动应用程序的执行,本文将详细分析Android应用程序的消息处理机制。有需要的朋友可以下载看看
立即学习“Python免费学习笔记(深入)”;
- 保存子任务引用,在父任务
finally块中显式调用child_task.cancel() - 使用
asyncio.gather(..., return_exceptions=True)并配合return_exceptions=False(默认)让任一子任务取消导致整个gather抛出CancelledError - 注意:已取消的任务再次
await会立即抛出CancelledError,无需重复 cancel
调试与诊断取消行为
当协程看似“没被取消”时,常见原因有:
- await 的对象不可取消(如自定义未继承
asyncio.Future的 awaitable) - 协程吞掉了
CancelledError却没重新抛出(例如except asyncio.CancelledError: pass) - 取消发生在任务已结束之后(
task.done()为True),此时cancel()返回False - 使用了
loop.run_until_complete()但未 await 任务本身,而是直接运行协程对象,导致无法通过 Task 控制生命周期
可通过 task.cancelled()、task.done() 和 task.exception() 检查任务最终状态,辅助定位问题。









