Python asyncio 事件循环机制详解

1次阅读

事件循环是asyncio的单线程核心调度器,通过轮询i/o、定时器及协程让渡控制权实现协作式并发;需避免阻塞操作,可用run_in_executor卸载cpu密集任务。

Python asyncio 事件循环机制详解

事件循环是 asyncio 的核心调度器

asyncio 不是靠多线程或多进程实现并发,而是通过一个单线程内的事件循环(Event Loop)来协调多个协程的执行。它不断轮询 I/O 状态(如 socket 是否可读/可写)、检查定时任务是否到期、调度已就绪的协程,把控制权在协程之间快速切换——这种模式叫“协作式并发”。关键点在于:协程必须主动让出控制权(比如用 await 等待 I/O),事件循环才有机会调度其他任务。

一个 python 进程通常只有一个默认事件循环

调用 asyncio.run(main()) 会自动创建、启动并关闭一个事件循环;手动管理时可用 asyncio.get_event_loop()(旧版)或更推荐的 asyncio.get_running_loop()(要求循环已在运行)。注意:不能在已有运行中的循环里再调用 asyncio.run(),否则报 RuntimeError: asyncio.run() cannot be called from a running event loop。多线程中每个线程需独立调用 asyncio.new_event_loop()set_event_loop(),因为事件循环不跨线程共享。

事件循环如何调度协程:从 create_task 到 await 完成

当你调用 asyncio.create_task(coro),协程对象被包装为 Task,并被加入事件循环的就绪队列(ready queue)或等待队列(waiting queue)。事件循环每一轮迭代(称为一个“tick”)会:

  • 执行所有已就绪的回调和 Task(即刚被唤醒、未阻塞的协程)
  • 检查文件描述符、socket、定时器等底层事件(通过 selector 或 proactor)
  • 将因 await 某个 Future 而挂起的协程,绑定到该 Future 完成时的回调上
  • 若当前无事可做且有 pending task,可能进入短暂休眠(如 select/poll 等待 I/O)

常见误区与调试提示

很多人误以为 await asyncio.sleep(1) 是“线程休眠”,其实它是注册一个延迟回调,让出控制权,事件循环可去干别的事;真正耗时的同步操作(如 time.sleep()json 解析、正则匹配)会阻塞整个事件循环。解决方法包括:

立即学习Python免费学习笔记(深入)”;

  • loop.run_in_executor() 把 CPU 密集型或阻塞调用扔进线程池
  • 避免在协程中直接调用阻塞函数
  • asyncio.current_task()asyncio.all_tasks() 查看正在运行的任务,辅助定位卡死问题
  • 启用 asyncio.get_event_loop().set_debug(True) 可捕获慢回调、未 await 的协程等潜在问题
text=ZqhQzanResources