asyncio.run()不能在已运行事件循环中调用,因它专为顶层脚本设计,会新建并关闭循环;在Jupyter、FastAPI等环境应改用asyncio.create_task()或await。

asyncio.run() 是最简测试方式,但不适用于已运行的事件循环
直接调用 asyncio.run() 是验证协程能否正常执行的最快路径,它会自动创建新事件循环、运行协程、关闭循环。但如果你已在 Jupyter、pytest 或 Django 开发服务器中——这些环境本身已启动了事件循环——再调用 asyncio.run() 会抛出 RuntimeError: asyncio.run() cannot be called from a running event loop。
此时必须改用 asyncio.create_task() 或显式获取当前循环:
- 在交互式环境(如 IPython)中,用
asyncio.get_running_loop().create_task(my_coro()) - 若不确定是否已有运行中的循环,先尝试
asyncio.get_running_loop();捕获RuntimeError后再 fallback 到asyncio.new_event_loop() -
asyncio.run()内部会调用loop.run_until_complete(),但你不该手动调用后者,除非你已通过asyncio.new_event_loop()创建并设置了循环
pytest 中测试 async 函数需用 pytest-asyncio 插件
原生 pytest 不识别 async def 测试函数,直接运行会报 TypeError: object async_test_func can't be used in 'await' expression(实际错误是测试被当作普通函数调用,未 await)。
正确做法是安装并启用 pytest-asyncio:
立即学习“Python免费学习笔记(深入)”;
- 安装:
pip install pytest-asyncio - 在
pyproject.toml中配置:[tool.pytest.ini_options] asyncio_mode = "auto"(Python 3.11+ 推荐) - 测试函数保持
async def test_something():,无需手动 await 或调用asyncio.run() - 避免在测试中混用
loop.run_until_complete()—— 插件已接管事件循环生命周期,手动干预易导致RuntimeError: Event loop is closed
手动管理事件循环时,注意 Python 3.11 的默认策略变更
Python 3.11 将 asyncio.DefaultEventLoopPolicy 改为基于 uvloop 的替代实现(仅当 uvloop 可用时),但更关键的是:调用 asyncio.set_event_loop_policy() 后,后续 asyncio.get_event_loop() 行为会受策略影响,而 asyncio.get_running_loop() 始终只返回当前线程中正在运行的那个循环(无论策略)。
常见误操作:
- 在子线程中调用
asyncio.get_event_loop()—— 它可能返回主线程的循环,或创建新循环,行为不稳定;应始终用asyncio.new_event_loop()+loop.set_event_loop()显式绑定 - 调用
loop.close()后仍尝试loop.run_until_complete()→RuntimeError: Event loop is closed - 忘记在
asyncio.new_event_loop()后调用loop.run_forever()或loop.run_until_complete()—— 循环处于“已创建但未启动”状态,协程不会执行
调试事件循环卡住?优先检查 await 对象是否真为协程
最常见的“循环不动”现象不是循环本身故障,而是 await 了一个非协程对象(比如忘了加 () 调用协程函数),导致 await 等待一个 coroutine object 实例而非执行它,结果等同于 await None(语法错误)或静默跳过(如果误用了生成器)。
快速排查方法:
- 打印
type(my_coro)—— 正确应为<class 'coroutine'>;若为<function ...>,说明漏了括号 - 用
inspect.iscoroutine()或inspect.iscoroutinefunction()做断言 - 在
asyncio.run()外层包一层try/except BaseException,捕获未处理的 CancelledError 或 KeyboardInterrupt —— 它们常因 await 链中断而被吞掉
Python 3.11 对协程类型检查更严格,某些在 3.9 下侥幸通过的误用(如 await 普通生成器)现在会直接抛 TypeError。


















