Python技术迷

asyncio 的事件循环(event loop)是如何运行的?

你可以把 event loop 想象成“一个永远在值班的调度员”。它的活儿其实很简单:不断盯着一堆任务(协程、回调、IO操作),看哪个准备好了,就立刻让它上 CPU 跑一会儿。然后再换下一个,就这么循环往复。

跟平时我们写的 for 循环不同,这玩意是“不会停”的。只要你没手动关掉,它就一直在跑,像个服务器进程一样。

启动的时候发生了啥?

最常见的用法大家都写过:

import asyncio

asyncdefsay_hello():
    print("hello ...")
await asyncio.sleep(1)
    print("world!")

asyncio.run(say_hello())

这里的 asyncio.run() 干了几件事:

  1. 创建一个新的事件循环(loop)。
  2. 把你传进去的那个协程任务塞到 loop 里。
  3. 启动 loop,不停轮询,看哪个协程 ready 了就执行。
  4. 等到任务都结束了,再把 loop 关掉,顺便清理下资源。

看起来就一句话,背后其实就是 loop 在死循环。大概逻辑类似这样(伪代码):

whileTrue:
    ready_tasks = get_all_ready_tasks()
for task in ready_tasks:
        run_task(task)
if no_more_tasks():
break

那些 await 背后发生的事

比如你写了 await asyncio.sleep(1),这时候 Python 并不是卡住一整秒,而是告诉事件循环: “哥们我先歇一秒,这期间你可以去跑别的任务,等一秒过去再叫我”。

于是 loop 就把这个协程挂起来,放进一个定时器队列里。等一秒时间到了,loop 再把它捞出来,继续执行后半段。

这就是所谓的“协作式切换”:谁主动 await,谁让出 CPU。

有点像 IO 多路复用

事件循环的核心就是不断等待 IO,就像 select/poll/epoll 那一套。loop 里会有个系统调用,等着 socket、管道、文件描述符啥的有事件发生,一旦触发了,loop 马上调度相关的协程去处理。

所以 asyncio 本质就是封装了一个跨平台的 IO 复用器 + 调度器。你写的协程代码,最后都会变成 loop 里的任务对象。

比如你要同时发起两个网络请求,如果是同步写法就是一个等一个,但在 asyncio 里,可以一起跑:

import asyncio

asyncdeffetch(name, delay):
    print(f"{name} start")
await asyncio.sleep(delay)
    print(f"{name} done after {delay}s")

asyncdefmain():
    tasks = [
        asyncio.create_task(fetch("task1", 2)),
        asyncio.create_task(fetch("task2", 1)),
    ]
await asyncio.gather(*tasks)

asyncio.run(main())

执行的时候,task1 和 task2 都会被 loop 调度。因为 task2 只需要 1 秒,它会先完成,然后 task1 再结束。整个流程 loop 全程盯着:谁准备好了就调度谁。

  • 事件循环 = 死循环 + 调度器,本质是个任务分配器。
  • await 是关键,它告诉 loop:我这边还没 ready,你先去忙别的。
  • 背后靠的是 IO 多路复用,才能做到“一核多用”。
  • 写异步代码时,真正的主角其实是 loop,我们写的协程只是被它管控的工人。

-END-

我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

🔥虎哥私藏精品🔥

虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB,点击下方公众号回复关键字 python 全部免费领