直接调用 loop.close() 会报 RuntimeError: Event loop is running 错误,因事件循环必须先 stop() 再 close();asyncio.run() 自动处理关闭但不可嵌套调用,手动管理需确保 stop 后 close 并清理待决任务。

直接调用 loop.close() 会报错:RuntimeError: Event loop is running
这是最常见的错误。当事件循环正在运行(比如你刚用 asyncio.run() 启动,或手动调用了 loop.run_forever()),直接调用 loop.close() 就会触发该异常。Python 要求事件循环必须处于“已停止”状态才能关闭。
正确做法是先停掉它:
- 如果用的是
asyncio.run()—— 不需要手动关,它内部自动stop()+close() - 如果手动管理循环(如
loop = asyncio.get_event_loop()),必须先调用loop.stop(),再调用loop.close() - 注意:
loop.stop()只是让循环退出下一次轮询,不会立即中断正在执行的协程;若需强制中止,应配合asyncio.cancel()或信号处理
asyncio.run() 是最安全的入口,但不适用于嵌套或复用场景
asyncio.run() 内部做了三件事:新建事件循环、运行主协程、运行结束后自动 stop() 和 close()。它设计初衷就是“一次一用”,适合脚本或 CLI 工具。
但它有硬性限制:
立即学习“Python免费学习笔记(深入)”;
- 不能在已有运行中的事件循环内调用(会抛
RuntimeError: asyncio.run() cannot be called from a running event loop) - 每次调用都新建循环,无法复用已存在的
loop实例 - 不支持自定义策略(如子进程、Windows Proactor 等特殊需求)
如果你在 Jupyter、Django shell 或已有异步框架(如 FastAPI 的测试环境)里调试,得换手动管理方式。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
手动关闭循环的可靠顺序:stop → close,且要确保无待决任务
手动管理时,典型流程是:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
loop.run_until_complete(main())
finally:
# 必须先 stop,再 close
loop.stop()
loop.close()
但更关键的是清理待决任务,否则 loop.close() 可能静默丢弃未完成的协程(尤其在 Python
- 调用
asyncio.all_tasks(loop)获取所有未完成任务 - 对每个任务调用
task.cancel(),再用loop.run_until_complete(asyncio.gather(*tasks, return_exceptions=True))等待它们结束 - 最后再
loop.stop()和loop.close()
注意:Python 3.12+ 在 loop.close() 前会自动 warn 未取消的任务,但不会帮你 cancel。
多线程环境下不能跨线程操作同一个事件循环
事件循环绑定到创建它的线程,loop.run_forever() 或 loop.run_until_complete() 必须在同一线程调用。如果你在子线程启动了循环,主线程试图 loop.stop(),会触发 RuntimeError: Cannot close a running event loop from a different thread。
跨线程通信只能通过线程安全机制:
- 用
loop.call_soon_threadsafe()把loop.stop()推入循环线程执行 - 不要把
loop对象传给其他线程直接调用方法 - 避免使用
asyncio.get_event_loop()在非创建线程中获取 —— 它可能返回新循环或报错
真正麻烦的不是怎么关,而是怎么让关的动作发生在正确的线程、正确的时机,且不遗漏还在跑的 task。漏掉一个 asyncio.create_task() 没 await 或 cancel,就可能让资源泄漏或程序 hang 住。

















