asyncio.run() 会自动管理事件循环的创建、运行和关闭,手动调用 loop.close() 会报 RuntimeError;需复用循环时应改用 asyncio.new_event_loop() 等底层 API,并确保 pending task 清理完毕后再 close。

asyncio.run() 之后再手动调用 loop.close() 会报错
很多人在用 asyncio.run(main()) 启动协程后,还想手动关掉事件循环,比如写个 loop = asyncio.get_running_loop(); loop.close() —— 这会直接抛出 RuntimeError: Cannot close a running event loop。因为 asyncio.run() 内部已经负责了创建、运行、关闭整套流程,你插手反而破坏它的状态管理。
需要手动控制事件循环时,别用 asyncio.run()
如果你的场景要求复用同一个事件循环(比如嵌入到 GUI 应用、测试框架或长期运行的服务中),就得绕过 asyncio.run(),改用底层 API:
- 用
asyncio.new_event_loop()创建新循环 - 用
loop.run_until_complete(main())或loop.run_forever()驱动它 - 结束前先调用
loop.stop()(如果还在run_forever中),再调用loop.close()
注意:调用 loop.close() 前,必须确保没有 pending task、没有正在运行的 callbacks,否则会警告甚至崩溃。建议加一层清理:
loop.run_until_complete(loop.shutdown_asyncgens()) loop.close()
asyncio.get_event_loop() 在 Python 3.10+ 已弃用
旧代码里常见 loop = asyncio.get_event_loop() 然后 loop.close(),这在 Python 3.10 起会触发 DeprecationWarning,3.12 开始已移除。正确做法是明确持有对 loop 的引用,或者用 asyncio.get_running_loop()(仅限循环已启动且当前线程中)。
立即学习“Python免费学习笔记(深入)”;
典型错误模式:
loop = asyncio.get_event_loop() # Python 3.12 报错:AttributeError loop.close()
替代写法(安全且向前兼容):
try:
loop = asyncio.get_running_loop()
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# … 启动逻辑 …
loop.close()在多线程中关闭事件循环要格外小心
每个线程默认没有事件循环,asyncio.get_running_loop() 只在当前线程有活跃 loop 时才返回它。如果你在子线程里创建并运行了 loop,主线程不能直接关它 —— 必须在线程内自己关,或者用 loop.call_soon_threadsafe(loop.stop) 触发停止,再 join 线程。
常见陷阱:
- 从主线程调用子线程 loop 的
close()→RuntimeError: There is no current event loop in thread - 没等所有 task 完成就调
loop.close()→ 资源泄漏,比如未关闭的 TCP 连接
稳妥做法是:在子线程中用 loop.run_forever(),外部通过 call_soon_threadsafe 发送 stop 信号,再显式 join(),最后在该线程内 close。
真正麻烦的不是“怎么关”,而是“关之前有没有漏掉还没 resolve 的 future、没 cancel 的 task、没 await 的 shutdown hook”。这些不处理干净,close() 表面成功,背后可能留一堆僵尸连接或未释放的文件描述符。


















