事件循环(Event Loop)的"电梯"比喻
把单线程 CPU 想成一部只载一人的电梯:
- 电梯里一次只能运行一个人(同步函数)
- 但大楼里 1000 个人都要去不同楼层(I/O 操作:网络、文件、数据库 ...)
- 如果每个人"自己开电梯"→ 电梯空跑、人堵门口,效率极低
事件循环就是**"电梯调度员"**:
- 把整栋楼的所有"上下楼请求"登记到一张任务清单
- 电梯每次只拉一个"能立刻出发"的人
- 当某人按下"去 15 楼"按钮后,电梯不会傻等他走到 15 楼再回来 ,而是
- 把"到达 15 楼后的事"写进清单(回调 / Future)
- 立刻拉下一位就绪的人
- 15 楼按钮亮起(I/O 完成)→ 调度员再把"后续任务"重新塞进电梯
结果:一部电梯(单线程)在任意时刻都载着"就绪"的人 , 而"等人"的时间被拿去干别的事,宏观上 1000 个人同时移动------这就是并发。
技术映射
| 比喻 | 真实术语 |
|---|---|
| 任务清单 | 就绪队列(ready queue) |
| 电梯 | 线程(主线程) |
| 按钮亮起 | 内核 epoll/select 返回可读/可写事件 |
| 到达后的事 | 回调、协程 resume、Future.set_result() |
| 调度员 | 事件循环对象 (asyncio.get_event_loop()) |
为什么能"异步"又"并发"
-
异步
调用
await sock.recv(1024)时,协程把"剩余代码"封装成回调挂到事件循环,主线程立刻去执行下一个就绪任务;
当 socket 缓冲区收到数据 → 事件循环把回调重新推进就绪队列 → 协程恢复。
因此调用者不会阻塞在 recv 上,形成"非阻塞"效果。
-
并发
单线程同一时刻只能跑一条 CPU 指令,
但 I/O 操作 99 % 时间都在等网卡、等磁盘、等数据库响应 ;
事件循环把这些"空闲时间"切片,让成千上万个协程轮流占用 CPU ,
从宏观时间轴看,它们像是"同时"进行,于是实现了高并发。
一张图秒懂
lua
主线程(单核)
┌──────────────────────────────┐
│ while True: │
│ ① 将就绪协程推进电梯 │←---┐
│ ② 运行直到遇到 I/O │ │
│ ③ 把"后续"注册到内核 + 循环 │ │
│ ④ 内核 epoll 通知完成 │----┘
└──────────────────────────────┘
电梯永远满载,但从不等人,吞吐量 ↑↑
结论
事件循环不是魔法,只是把**"等 I/O 的时间"转化为 "运行其他任务的时间";
在单线程里让 大量 I/O 密集任务交替推进**,
既避免了多线程切换开销,又达到近乎线性的并发能力 ------
这就是它成为 Node.js、Python asyncio、FastAPI 灵魂的原因。