用途:这是一份 asyncio 内部机制参考,面向排查事件循环挂起、任务调度异常、call_soon 与 call_soon_threadsafe 混用、Future 回调时序、_enter_task/_leave_task 冲突等问题,基线为 CPython 3.11,并标注 3.12+(eager task factory、eager_start)和 3.13+(free-threaded、per-thread task state)差异。
主要能力:讲解事件循环 _run_once 的循环流程,包括处理 _scheduled 堆、通过 selector 轮询 I/O、处理 _ready deque,并说明 ntodo 快照机制导致回调需两个完整周期。解释超时选择逻辑:_ready 或 _stopping 非空时 timeout 为 0,有 _scheduled 时取最近到期时间,否则阻塞。对比 call_soon 与 call_soon_threadsafe:二者都追加到 _ready,但只有后者会唤醒 selector 并线程安全。说明跨线程 create_task 后事件循环卡在 select 的“停滞”模式,修复方式是调用 loop.call_soon_threadsafe(lambda: None) 唤醒。
说明 Future.set_result 不会同步触发回调,而是通过 call_soon 调度,回调会留在 _ready 中;对比自定义同步 Future 的即时派发及其 GIL 安全要求。介绍 Task.init 调用 call_soon(self.__step) 的调度结构,以及 3.12+ 的 eager_start=True 通过 _swap_current_task 内联执行协程;并提醒 3.12+ C 结构要求必须调用 Task.init,否则字段未初始化可能崩溃。
还涵盖 _enter_task/_leave_task 的单任务约束与 RuntimeError 冲突,A5 反模式(跨 GIL 释放持有 _enter_task)、哨兵 __step 回调这一主要冲突来源,以及 3.11 用 ready.pop()、3.12+ 用 eager_start 消除哨兵;说明按步而非按驱动加锁的粒度、tokio_driving 标志缓解跨线程冲突的残留 TOCTOU 风险;并提及 _set_running_loop 的线程本地状态、contextvars 的每驱动进入退出,以及 free-threaded 构建在 3.14 前需持续持有 GIL。
用法:文档以源码位置(如 Lib/asyncio/base_events.py:BaseEventLoop._run_once)和可通过 uv run python -c "..." 运行的验证片段呈现,示例命令保持原样。
限制:内容属于内部机制说明,不承诺特定性能或兼容性结果;具体版本行为、修复状态以官方源码与 issue 为准,其他事实暂未提供。