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

为何Asyncio事件循环的迭代次数超出预期?

你的理解偏差:事件循环的_run_once是完整轮询周期,而非协程切换次数

你预期的2次是协程的“执行-暂停”和“恢复-结束”两次切换,但事件循环的_run_once方法对应的是一次完整的轮询处理周期,这个周期包含多个步骤,远不止协程的单次执行片段。结合CPython 3.11.4的事件循环逻辑,四次_run_once调用的来源如下:

四次迭代的具体流程

假设你的测试代码是类似这样的标准协程:

import asyncio

async def test_coro():
    await asyncio.sleep(0.1)

asyncio.run(test_coro())

四次_run_once的触发场景分别是:

  1. 第一次迭代:
    事件循环启动后,首先调度初始协程任务,执行test_coro直到await asyncio.sleep(0.1)。此时协程挂起,sleep会注册一个定时器回调到事件循环,本次轮询周期结束。

  2. 第二次迭代:
    事件循环没有就绪的任务或IO事件,进入IO等待(比如epoll_wait),直到定时器到期。到期后,事件循环处理定时器回调,将sleep对应的Future标记为完成,并把协程重新加入就绪任务队列,本次轮询周期结束。

  3. 第三次迭代:
    事件循环发现就绪的协程任务,调度并执行它,直到协程完全结束。此时任务标记为完成,但事件循环还需要确认是否有其他待处理的任务或回调,本次轮询周期结束。

  4. 第四次迭代:
    事件循环检查所有任务和回调,确认没有未完成的工作,触发退出前的最后一次轮询(包括清理内部状态、检查是否有遗留回调等),随后事件循环终止。

核心偏差点

你混淆了协程的执行阶段数和事件循环的轮询周期数:

  • 协程确实只有两次主要执行阶段(到await暂停、唤醒后结束),但这两个阶段并不直接对应两次_run_once。
  • 每次_run_once会完成一整套流程:处理就绪回调、等待IO/定时器、处理到期事件、调度新任务,这些步骤可能跨越协程的多次状态切换。

事件循环的设计目标是一次轮询处理所有当前就绪的事件,而不是为每个协程切换单独启动一轮轮询,这也是为什么你会看到比预期更多的_run_once调用次数。

内容的提问来源于stack exchange,提问作者Andres Ariza.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 14:57:34