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

asyncio内部Task取消机制疑问:未await的Task为何执行

关于asyncio Task取消与协程执行时机的疑问

问题背景

已知task.cancel()仅设置取消标记,事件循环下一次迭代时会向包装的协程抛出CancelledError,且仅在await语句处才能向协程抛出该异常,协程需处于挂起状态才能触发。

但以下代码中,main()协程创建了coro_1的Task却未进行await,main()结束后coro_1却执行了并触发了取消错误:

import asyncio

async def coro_1():
    print("inside coro_1")
    try:
        await asyncio.sleep(0)
    except asyncio.CancelledError:
        print("cancellation error occurred")
        raise

async def main():
    print("inside main")
    new_task = asyncio.create_task(coro_1())
    print("finishing main")

asyncio.run(main())

输出结果:

inside main
finishing main
inside coro_1
cancellation error occurred

推测是asyncio.run()导致此现象,但查看源码后仍有疑惑:分析发现asyncio.run()内部会执行coro.throw(exc),但coro_1还未执行到await asyncio.sleep(0),按道理无法抛出异常,请问是谁在何时将coro_1执行到了那一行?(使用Python 3.11版本)


问题解析

1. asyncio.run()的收尾逻辑

asyncio.run()在跑完传入的主协程(main())后,不会直接退出事件循环,而是会自动处理所有未完成的Task:

  • 先给所有未完成的Task调用cancel()打上取消标记;
  • 然后事件循环会强制调度这些Task运行,直到它们进入可取消的挂起状态——也就是遇到await语句的位置。

2. coro_1的执行时机

当main()结束后,事件循环进入收尾流程:

  • 它会调度coro_1这个Task开始执行,一路运行到await asyncio.sleep(0)这一行;
  • asyncio.sleep(0)会让协程立刻挂起(它不会真的休眠,只是主动让出控制权),此时coro_1满足了抛出CancelledError的条件;
  • 之前设置的取消标记随即触发异常,于是就出现了你看到的输出顺序。

3. 关键细节:为什么await asyncio.sleep(0)是触发点

await asyncio.sleep(0)是协程从“运行中”切换到“挂起”状态的节点,只有在挂起状态下,事件循环才能向协程抛出CancelledError。如果coro_1里没有任何await语句,那它会被完整执行完,不会触发取消错误——因为协程全程处于运行状态,没有给事件循环抛出异常的机会。


内容的提问来源于stack exchange,提问作者S.B

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 19:04:51