Python3.11 asyncio CancelledError行为解惑:为何二次sleep被取消三次却完成?
先看你提供的代码:
import asyncio as aio async def cancel_me(): while True: try: await aio.sleep(1) # 1. sleep except aio.CancelledError: print(f"Cancelled {aio.current_task().get_name()}") try: await aio.sleep(1) # 2. sleep except aio.CancelledError: print("Second sleep cancelled") await aio.sleep(1) # 3. sleep print("Third sleep NOT cancelled") raise async def main(): tsk = aio.Task(cancel_me()) await aio.sleep(0.1) tsk.cancel() aio.run(main())
执行后输出:
Cancelled Task-2 Second sleep cancelled Third sleep NOT cancelled
核心原因:asyncio任务取消的「一次性信号+状态重置」机制
asyncio的任务取消不是持续生效的,而是通过单次信号+标记重置的逻辑工作:
发送取消信号
main里调用tsk.cancel()后,cancel_me任务的取消标记被设为True。此时任务正卡在标注1的sleep(1),sleep被中断,抛出CancelledError,进入第一个except块。标注2的sleep被取消
进入第一个except后,任务的取消标记仍为True——因为我们还没处理完这次取消信号(既没重新抛出异常,也没让任务结束)。此时执行标注2的sleep(1),await操作会检查任务取消状态,发现标记为True,立刻抛出CancelledError,进入第二个except块。取消标记被自动重置
当捕获到第二次CancelledError后,asyncio会自动把任务的取消标记重置为False——这是框架的设计规则:每次捕获CancelledError后,取消状态就会被清除,除非你主动重新抛出这个异常。标注3的sleep正常执行
此时取消标记已经是False,执行标注3的sleep(1)时,await操作不会触发取消逻辑,所以sleep能正常等待1秒完成,之后打印Third sleep NOT cancelled。最后代码主动raise重新抛出CancelledError,任务才最终结束。
补充验证
如果在第二个except块里重新抛出CancelledError,取消标记不会被重置,标注3的sleep也会被取消:
# 修改第二个except块 except aio.CancelledError: print("Second sleep cancelled") raise # 重新抛出异常,取消标记保持为True
此时标注3的sleep会立刻抛出异常,不会执行到打印语句。
内容的提问来源于stack exchange,提问作者nilss

