事件循环asyncio的心脏

一、事件循环 —— asyncio 的心脏

1.1 事件循环是什么?

 
事件循环 = 一个死循环 + 任务队列 + 就绪检查
 
while True:
 
    ① 检查哪些协程就绪了(之前 await 的 I/O 完成了)
 
    ② 挑一个就绪的协程 → 执行它
 
    ③ 协程运行中遇到 await → 暂停 → 把控制权交还给事件循环
 
    ④ 回到 ①
 
 
# 事件循环的核心逻辑(极度简化——帮你理解,不是源码)
 
class SimpleEventLoop:
 
    def __init__(self):
 
        self.ready = []     # 就绪的协程
 
    def run_until_complete(self, coro):
 
        self.ready.append(coro)
 
        while self.ready:                    # ① 死循环——直到没有就绪任务
 
            task = self.ready.pop(0)         # ② 取出一个就绪协程
 
            try:
 
                task.send(None)              # ③ 恢复执行——底层就是生成器的 send()
 
            except StopIteration:
 
                continue                     # ④ 协程执行完毕——跳过
 
            # ⑤ 如果协程内部 await 了 → send() 返回(暂停)→ 回到循环顶部
 
            #    真实的事件循环会把 await 的东西注册到 I/O 多路复用(epoll/select)
 
            #    等 I/O 就绪后再把协程放回 ready 队列
 

1.2 事件循环的三个核心操作


graph TD

    subgraph 事件循环 死循环

        A[① 从就绪队列取一个协程]

        B[② 执行 直到协程 await]

        C[③ 注册 IO 等待]

        D[④ 回到 ①]

        A --> B --> C --> D

    end

    E[await] --> B

    F[IO 完成] -->|放回就绪队列| A

操作谁触发做了什么
挂起(suspend)协程执行到 await协程暂停 → 控制权还给事件循环 → 事件循环去执行其他协程
注册(register)事件循环把 await 的 I/O 操作注册到操作系统(epoll/select)——“这个 socket 有数据了通知我”
唤醒(resume)OS 通知 → 事件循环I/O 就绪 → 事件循环把协程放回就绪队列 → 下次轮到它就继续执行

1.3 一张图看懂 3 个协程的调度


sequenceDiagram

    participant EvtLoop as 事件循环

    participant A as 协程A

    participant B as 协程B

    participant C as 协程C

    EvtLoop->>A: 执行

    A->>A: await IO 暂停

    EvtLoop->>B: 执行

    B->>B: await IO 暂停

    EvtLoop->>C: 执行

    C->>C: await IO 暂停

    Note over EvtLoop: IO 回调触发

    EvtLoop->>B: B 就绪 继续执行

    B->>B: await 暂停

    EvtLoop->>C: C 就绪 继续执行

    Note over C: 完毕

    EvtLoop->>A: A 就绪 继续执行

    Note over A: 完毕

核心认知:事件循环不会同时执行两个协程。它只是在就绪的协程之间快速切换。谁就绪了谁跑——这叫”协作式多任务”。

1.4 事件循环和 JS 事件循环的区别

你学过 JS 事件循环——两个”事件循环”名字一样,但物理结构完全不同。

Python asyncio 事件循环JS 浏览器事件循环
调度对象协程(coroutine)宏任务/微任务
触发机制await 主动交出控制权函数执行到结束才交
抢占性协作式——协程自己决定何时暂停非抢占式——函数必须跑完
多线程可在多线程中运行多个事件循环单线程(Web Worker 除外)
底层 I/Oepoll/select(操作系统级)浏览器提供的 Web API

一句话:Python 的协程在 await 处主动暂停——像接力赛自己交棒;JS 的函数必须跑完才能交——像每个人必须跑完自己那段。


▶ 对应实操:14-TypedDict

速记卡(面试闪卡)

Q1:一句话讲清「事件循环asyncio的心脏」到底是什么?

A:事件循环是 asyncio 的调度核心,本质是死循环加任务队列,在就绪协程间快速切换实现协作式多任务。

Q2:二、事件循环 —— asyncio 的心脏 —— 怎么理解?

A:像餐厅领班:事件循环(Event Loop)本质是”死循环加任务队列加就绪检查”,不停在就绪协程间切换,谁 await 的 IO 完成谁就跑,从不同时跑两个协程。

Q3:1.1 事件循环是什么 —— 怎么理解?

A:像不停巡逻的领班:简化版就是 while True——检查哪些协程就绪、挑一个执行、遇 await 暂停交还控制权、再回到检查,底层靠生成器 send(None) 恢复协程。

Q4:1.2 三个核心操作(挂起/注册/唤醒) —— 怎么理解?

A:像三步接力:挂起(Suspend)协程 await 时暂停交权;注册(Register)把 IO 交给操作系统 epoll/select”好了通知我”;唤醒(Resume)IO 就绪后放回就绪队列继续跑。

Q5:1.4 事件循环和 JS 事件循环的区别 —— 怎么理解?

A:像接力赛 vs 跑完全程:Python 协程在 await 主动交棒(协作式);JS 函数必须跑完才交权(非抢占式)。Python 调度协程、可多线程多循环;JS 调度宏微任务、通常单线程。

Q6:核心速记主线有哪些?

  • 事件循环本质:死循环加任务队列加就绪检查,在就绪协程间快速切换(协作式多任务)

  • 三操作:suspend(await 暂停交权)、register(IO 注册到 epoll/select)、resume(IO 就绪唤醒)

  • 事件循环永不并行执行两个协程,故擅长 IO 密集、不擅长 CPU 密集

  • 与 JS 事件循环不同:Python 协程 await 主动交棒,JS 函数跑完才交权

口诀

A:事件循环一颗心,死循环里排任务

await 暂停交棒去,就绪再来接着轮

挂起注册再唤醒,epoll 通知才回魂

协程不并行,IO 密集它最神

相关链接

相关链接