事件循环:Asyncio 的大脑
事件循环是 Python asyncio 库的核心——它是决定何时运行哪段代码的总指挥。你可以把它想象成一个聪明的调度器或分发者,高效地管理和切换多个任务。
事件循环到底是什么?
事件循环本质上就是一个循环(顾名思义),它会:
- 检查哪些任务已经准备好可以运行
- 运行这些任务,直到它们主动让出控制权
- 处理 I/O 操作,比如网络或文件访问
- 管理定时器,实现延迟执行
- 跟踪所有任务,确保没有遗漏
它就像你代码的空中交通管制员,确保一切顺畅运行,不会发生冲突。
交通管制的类比
想象一个繁忙的十字路口,有一个红绿灯:
- 十字路口 就是你的 CPU——同一时刻只能有一辆车通过
- 红绿灯 就是事件循环——它决定哪辆车什么时候可以通过
- 汽车 就是你程序中等待运行的任务
- 不同道路上的汽车 代表不同类型的任务(网络、文件 I/O、定时器等)
红绿灯会高效地在不同道路间切换,让汽车尽可能顺畅地通过,而不是等一条路的车全部走完才轮到下一条路。
核心事件类型
事件循环主要处理以下几类事件:
- I/O 事件——当有数据可以从 socket 或文件中读取或写入时
- 定时器事件——如
asyncio.sleep()
等延迟到期时
- Future/任务完成事件——某个异步操作完成时
- 信号事件——需要处理的操作系统信号
事件循环的简化视图
下面是事件循环工作原理的一个简化版本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
def event_loop():
ready_tasks = deque() # 已准备好运行的任务
io_waiting = {} # 等待 I/O 的任务
timers = [] # 等待定时器的任务
while tasks_exist():
# 运行所有已准备好的任务,直到它们让出控制权
while ready_tasks:
task = ready_tasks.popleft()
result = task.run()
if result.is_complete:
mark_complete(task)
elif result.waiting_for_io:
io_waiting[result.resource] = task
elif result.waiting_for_timer:
timers.append((result.wake_time, task))
# 找到下一个定时器到期的时间
next_timer = earliest_timer_time()
# 等待 I/O 或定时器(以先到者为准)
ready_resources = wait_for_io_or_timer(next_timer)
# 将 I/O 就绪的任务移到 ready 队列
for resource in ready_resources:
ready_tasks.append(io_waiting.pop(resource))
# 将定时器到期的任务移到 ready 队列
current_time = get_current_time()
for time, task in get_expired_timers(current_time):
ready_tasks.append(task)
|
本质上,这就是让 asyncio 运转的核心算法。
如何使用事件循环
虽然 asyncio 通常会自动帮你管理事件循环,但你也可以手动与之交互:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import asyncio
# 获取当前事件循环
loop = asyncio.get_event_loop()
# 在事件循环中运行一个协程
result = loop.run_until_complete(my_coroutine())
# 安排一个回调函数
loop.call_soon(my_callback)
# 安排一个延迟回调
loop.call_later(5, my_callback) # 5 秒后运行
|
在现代 Python 中,通常推荐使用更高级的函数,比如 asyncio.run()
,它会自动帮你管理事件循环。
理解事件循环的工作原理,可以帮助你深入理解 asyncio 的行为,从而写出更高效的异步代码。