You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python3.11 asyncio CancelledError行为解惑:为何二次sleep被取消三次却完成?

Python 3.11异步任务取消行为解析

先看你提供的代码:

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的任务取消不是持续生效的,而是通过单次信号+标记重置的逻辑工作:

  1. 发送取消信号
    main里调用tsk.cancel()后,cancel_me任务的取消标记被设为True。此时任务正卡在标注1的sleep(1),sleep被中断,抛出CancelledError,进入第一个except块。

  2. 标注2的sleep被取消
    进入第一个except后,任务的取消标记仍为True——因为我们还没处理完这次取消信号(既没重新抛出异常,也没让任务结束)。此时执行标注2的sleep(1),await操作会检查任务取消状态,发现标记为True,立刻抛出CancelledError,进入第二个except块。

  3. 取消标记被自动重置
    当捕获到第二次CancelledError后,asyncio会自动把任务的取消标记重置为False——这是框架的设计规则:每次捕获CancelledError后,取消状态就会被清除,除非你主动重新抛出这个异常。

  4. 标注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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 20:05:13