asyncio.run() 是一次性启动器,每次新建并关闭事件循环,适合脚本顶层入口;get_event_loop() 是手动管理入口,已软弃用,需自行处理生命周期。

asyncio.run() 是“一次性启动器”,get_event_loop() 是“手动循环管理入口”
asyncio.run() 的设计目标就是开箱即用:它内部调用 asyncio.new_event_loop() 创建全新循环,执行传入的协程,再自动调用 loop.close()。整个过程不可干预、不可复用。而 get_event_loop() 只是尝试返回当前线程绑定的默认循环(Python 3.10+ 已软弃用),不创建、不关闭、不隔离——你得自己处理生命周期,漏掉 loop.close() 就可能让后续调用报 RuntimeError: Event loop is closed。
- asyncio.run() 每次都新建 + 关闭,适合脚本顶层主程序入口
-
get_event_loop()返回的是可复用的循环实例,但行为在多线程、Jupyter、嵌入环境里极不可靠 - Python 3.7+ 推荐在协程内部用
asyncio.get_running_loop()替代get_event_loop() - 如果你需要控制 loop 实例(比如用 uvloop),
asyncio.run()不支持传入自定义 loop,必须退回new_event_loop()+set_event_loop()手动流程
在 Jupyter 或 Web 框架里调用 asyncio.run() 会直接报错
因为这些环境本身已启动并运行着一个事件循环。asyncio.run() 内部检测到已有运行中的 loop,立刻抛出 RuntimeError: asyncio.run() cannot be called from a running event loop。这不是配置问题,是 API 层面的硬性限制。
- Jupyter Notebook 默认内核已运行 loop,重复执行
asyncio.run(main())必然失败 - Django 4.1+ 异步视图、FastAPI 路由、Starlette 中间件等,协程已在框架管理的 loop 中执行,此时再调
asyncio.run()就是双重嵌套 - 正确做法是直接
await main(),或用asyncio.create_task()提交子任务 - 临时 workaround(仅调试):装
nest_asyncio并调nest_asyncio.apply(),但它只是打补丁,不解决根本逻辑
get_event_loop() 在子线程里默认失效
get_event_loop() 默认只返回主线程绑定的 loop。子线程中首次调用它,Python 3.7+ 会返回新 loop(但不设为默认),而 Python 3.6 及更早版本直接返回 None —— 导致 AttributeError 或静默失败。
- 子线程中安全做法是显式调用
asyncio.new_event_loop(),再用asyncio.set_event_loop()绑定 - 或者直接用
asyncio.get_running_loop()(需确保 loop 已启动,否则报RuntimeError: no running event loop) - 不要依赖
_event_loop这类私有属性,它不是公开 API,版本升级后可能消失或语义变更 - 高频短任务(如每秒调度多个异步请求)反复调
asyncio.run()开销明显,此时应自己管理单个 loop 生命周期
混用阻塞调用会让 asyncio.run() 启动的循环“假死”
asyncio.run() 确实能干净启动循环,但如果协程里写了 time.sleep(1)、requests.get() 或普通文件读写,整个事件循环会被卡住——因为这些是同步阻塞操作,不把控制权交还给 loop。
立即学习“Python免费学习笔记(深入)”;
- 延时必须用
await asyncio.sleep(1),不是time.sleep() - HTTP 请求必须用
aiohttp或httpx.AsyncClient,不能 awaitrequests.get() - CPU 密集型操作要用
loop.run_in_executor()拆到线程池,否则协程无法让出 - 文件 IO 推荐
aiofiles库,或 Python 3.9+ 的await asyncio.to_thread(open, ...)
asyncio.run() 表面成功、实际失效。


















