应直接更换默认事件循环:windows用proactoreventloop避开select()限制,linux/macos用uvloop提升性能;二者均需在asyncio调用前显式设置且不可混用。

直接换掉默认事件循环,而不是试图修 SelectorEventLoop 的跨平台行为——它在 Windows 上本就不该被当作高性能方案用。
为什么 Windows 下的 select() 会拖慢 asyncio
Windows 的 select() 系统调用只支持 socket 类型的文件描述符,且最大句柄数硬限制为 512(实际常更低)。当你的异步服务打开大量连接或混用管道、信号量等非 socket fd 时,select() 会直接抛出 OSError: [WinError 10038] an operation was attempted on something that is not a socket 或静默失效。Linux/macOS 的 epoll/kqueue 没这问题,但 Windows 默认策略仍 fallback 到 SelectSelector。
- asyncio 在 Windows 上默认使用
SelectorEventLoop+SelectSelector,这是历史兼容性选择,不是性能选择 -
select()调用本身是线性扫描所有注册 fd,fd 数量一过百,延迟就明显上升 - 即使你手动创建
ProactorEventLoop(Windows 原生异步 I/O),它也不支持subprocess的某些信号交互,容易在asyncio.create_subprocess_exec场景崩溃
用 uvloop 替代默认循环(Linux/macOS 推荐)
uvloop 是 libuv 的 Python 封装,把 epoll/kqueue 的能力带进 asyncio,同时屏蔽了 selector 层的实现细节。它不依赖 OS 的 select(),自然绕开 Windows 兼容性问题——不过注意:uvloop 官方 不支持 Windows,所以这个方案只适用于部署在 Linux/macOS 的服务。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- 安装:
pip install uvloop - 启用方式(必须放在脚本最开头,早于任何 asyncio 调用):
uvloop.install() - 验证是否生效:
print(asyncio.get_event_loop_policy().__class__)应输出<class></class> - 不兼容场景:如果你用了
asyncio.set_event_loop_policy()自定义策略,或依赖某些仅 selector 模式下才工作的调试钩子(如部分trio兼容层),需额外测试
Windows 下安全回退到 ProactorEventLoop
Windows 自身的 IOCP(I/O Completion Ports)机制比 select() 高效得多,ProactorEventLoop 就是它的封装。它不走 selector 路径,因此完全避开 SelectSelector 的限制和 bug。但要注意:它对子进程、信号、TTY 控制的支持弱于 selector 模式。
- 显式设置(推荐在入口处判断):
if sys.platform == "win32": asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) - 避免混合使用:不要在同一个进程中先创建
SelectorEventLoop再切换策略,否则可能触发RuntimeError: cannot add child handler, the event loop is closed - 关键限制:Windows 的
ProactorEventLoop不支持loop.add_signal_handler(),如果代码里监听了SIGINT或SIGHUP,得改用loop.create_task()+asyncio.sleep()轮询替代
跨平台启动时统一策略的写法
别靠文档猜,用运行时检测+显式覆盖。asyncio 的策略设置是全局单例,一旦事件循环被创建(比如调用了 asyncio.run()),再设策略就无效。所以策略初始化必须绝对前置。
- 正确顺序:
import sys, asyncio→if sys.platform == "win32": ...→import uvloop; uvloop.install()(Linux/macOS)→asyncio.run(main()) - 不要在
main()函数里设策略,那已经晚了;也不要依赖asyncio.get_event_loop()来“修复”已存在的 loop - CI/CD 中建议加一行检查:
assert isinstance(asyncio.get_event_loop(), (asyncio.ProactorEventLoop, uvloop.Loop)), "event loop policy mismatch"
真正难处理的不是 selector 本身,而是那些隐式依赖 SelectSelector 行为的第三方库——比如某些老版本的 aiohttp 在 Windows 下会绕过事件循环策略,直接调用 select.select()。这种时候,要么升级库,要么在启动前 monkey patch selectors.SelectSelector.select,但后者风险极高,不如直接切容器跑 Linux 环境。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










