uvloop 是基于 libuv 的 asyncio 高性能 c 扩展事件循环,吞吐量提升 2–4 倍,需显式安装启用且 linux/macos 效果显著;windows 支持有限,版本兼容性敏感,应于主入口安全切换。

uvloop 是什么,为什么值得换掉默认事件循环
uvloop 是 asyncio 的 C 扩展实现,用 libuv 重写了事件循环核心,实测吞吐量通常比默认 asyncio.SelectorEventLoop 高 2–4 倍,尤其在高并发 I/O 场景(如 Web 服务、代理网关)下差异明显。
它不是“开箱即用”的替代品——你得显式安装并启用,且部分 asyncio 内部行为(比如某些调试钩子)可能被绕过。不兼容的典型表现是:asyncio.get_event_loop() 返回的仍是默认 loop,除非你主动干预。
- 必须
pip install uvloop,纯 python 环境不生效 - 启用方式只有两种可靠路径:
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())(全局生效),或启动时加-m uvloop(仅限脚本入口) - Windows 下需注意:libuv 对 IOCP 支持有限,
uvloop在 Windows 上实际性能提升远不如 Linux/macOS,甚至个别版本会触发RuntimeError: this event loop is already running
怎么安全地在现有 asyncio 项目里接入 uvloop
直接改 asyncio.set_event_loop_policy() 很容易出问题,尤其项目已依赖 asyncio.get_event_loop() 或使用了第三方库(如 aiohttp、fastapi)的内部 loop 初始化逻辑。
最稳妥的做法是只在应用真正启动前一刻切换,且避开测试环境和 REPL 调试场景。
立即学习“Python免费学习笔记(深入)”;
- 不要在模块顶层 import 后立刻调用
set_event_loop_policy,否则单元测试可能复用该 policy 导致干扰 - 推荐写法:在主函数入口(如
if __name__ == "__main__":块内)第一行设置,例如:import uvloop<br>asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())<br>asyncio.run(main()) -
asyncio.run()本身会新建 loop,所以它和 uvloop 兼容;但若你手动调用loop.run_until_complete(),必须确保 loop 是 uvloop 实例,否则会 fallback 到默认 loop
uvloop 和 asyncio 版本、Python 版本的隐性绑定关系
uvloop 不是“一次编译,到处运行”,它和 Python 解释器 ABI、asyncio 内部结构强耦合。升级 Python 或 asyncio 后,uvloop 很可能静默降级回默认 loop,或者直接报 ImportError: cannot import name '...。
- 查兼容性最准的方式是看
uvloop的pyproject.toml或 github Releases 页面,例如uvloop==0.19.0明确要求python >=3.8, - Python 3.12 引入了新的 task scheduling 机制,
uvloop==0.19.x无法工作,必须升到0.20.0+;但0.20.0又不支持 Python 3.8,这种断层很常见 - 如果你用的是打包工具(如 PyInstaller),要额外确认
uvloop的二进制 wheel 是否被正确嵌入,否则生产环境可能因找不到libuv动态库而 fallback
性能没变快?先检查这三件事
用了 uvloop 却没看到 QPS 提升,大概率不是 uvloop 不行,而是瓶颈根本不在事件循环上。
- 用
python -m asyncio --debug运行,观察输出里是否有大量Executing <task ...> took 0.x seconds</task>—— 如果单个协程耗时长,说明 CPU 或同步阻塞(如time.sleep()、未异步化的 DB 驱动)才是瓶颈,换 loop 没用 - 检查是否误用了
loop.run_in_executor()且线程池太小,导致大量请求排队等待线程,此时应调大max_workers或改用concurrent.futures.ProcessPoolExecutor - HTTP 客户端(如
aiohttp)默认连接池大小是 100,如果压测并发远超此数,会卡在连接等待上,和 loop 无关;需显式配置connector = aiohttp.TCPConnector(limit=1000)
uvloop 的价值在「把 I/O 多路复用做到极致」,但它不解决慢 SQL、没缓存、序列化开销大、GIL 争抢这些问题。真要榨干性能,得一层层往下挖,而不是指望一个 loop 替换就万事大吉。