macos 12+因kqueue限制导致asyncio.get_event_loop()报runtimeerror,需显式设selectoreventlooppolicy;uvloop在linux高并发i/o下快2–4倍但不支持windows;windows应改用windowsselectoreventlooppolicy避免卡顿。

asyncio.get_event_loop() 在 macOS 上为什么总报错 RuntimeError?
macOS 12+(Monterey 起)默认禁用 kqueue 的某些扩展行为,而 Python 3.8–3.11 的默认事件循环策略在调用 asyncio.get_event_loop() 时可能触发不兼容的系统调用,表现为:RuntimeError: There is no current event loop in thread 或更隐蔽的 OSError: [Errno 45] Operation not supported。
这不是代码写错了,是策略和内核行为对不上。解决方法不是换写法,而是显式指定循环实现:
- 强制使用
SelectorEventLoop(兼容性最好):import asyncio asyncio.set_event_loop_policy(asyncio.SelectorEventLoopPolicy())
- macOS 上可尝试
AsyncIOEventLoopPolicy(仅 Python 3.12+ 原生支持,旧版本会 fallback) - 避免在非主线程里调用
get_event_loop(),改用asyncio.get_running_loop()(Python 3.7+)
Linux 下 uvloop 比默认循环快多少?值得切吗?
uvloop 在高并发 I/O 场景(如 Web 服务、大量短连接)下,通常比 CPython 默认的 SelectorEventLoop 快 2–4 倍,核心原因是用 Cython 重写了关键路径,并绕过部分 Python 解释器开销。但它不是万能加速器:
- 只对纯异步 I/O 密集型有效;如果任务里混了大量 CPU 计算或同步阻塞调用,uvloop 优势几乎归零
- 不支持 Windows(会直接 ImportError),Linux/macOS 可用,但 macOS 上需注意:uvloop 0.19+ 才完整适配 Apple Silicon
- 启用方式极简:
import uvloop asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
- 调试时建议关掉 uvloop——它的错误堆栈比原生循环更难定位,尤其涉及
asyncio.Lock或自定义Future时
Windows 上 asyncio.run() 启动慢,且偶尔卡住?
Windows 默认用 ProactorEventLoop,它依赖 IOCP,但在某些场景(比如启动时立刻创建大量 asyncio.create_task()、或与 subprocess/stdin 混用)会出现初始化延迟甚至死锁。常见现象是 asyncio.run(main()) 卡在第一行不动,或报 WindowsError: [Errno 10038] An operation was attempted on something that is not a socket。
最稳妥的做法是退回到更稳定的 SelectorEventLoop:
- 全局设置(推荐放在脚本最开头):
import asyncio if hasattr(asyncio, 'WindowsSelectorEventLoopPolicy'): asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - 不建议用
asyncio.ProactorEventLoop处理标准输入/输出流,它对sys.stdin支持有限;读终端输入请用asyncio.to_thread(input)或第三方库如prompt_toolkit - PyInstaller 打包后出现该问题?多半是打包时没自动识别 uvloop 或策略模块,手动在 spec 文件里加
hiddenimports=['uvloop', 'asyncio.windows_events']
不同 Python 版本对循环策略的默认行为差异
Python 3.12 把事件循环策略从“隐式默认”变成“显式声明”,意味着你不能再依赖 asyncio.get_event_loop() 在任意线程都返回一个可用循环。这个变化让跨平台行为更一致,但也暴露了老代码里的隐式假设:
- 3.11 及以前:主线程首次调用
get_event_loop()会自动创建并设置默认循环;3.12 起必须先调用asyncio.set_event_loop_policy()或显式asyncio.new_event_loop() -
asyncio.run()在所有版本中都新建并关闭循环,但它内部仍受当前策略影响——比如你在 3.12 里没设策略,run()会用DefaultEventLoopPolicy,但该策略在 Windows 上可能 fallback 到Proactor,导致子进程异常 - 检查当前策略:打印
asyncio.get_event_loop_policy()的类型,而不是靠文档猜;运行时动态切换策略是安全的,但只对后续新创建的循环生效,不影响已运行的run()
跨平台项目里最容易被忽略的,是把“循环策略”当成一次性配置项,其实它和线程绑定——每个线程可以有自己的策略实例,多线程协程调度时得格外小心。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











