排查Python3.6.5中aiohttp、asyncio与Spyder3的RuntimeError问题
解决aiohttp的"RuntimeError: This event loop is already running"错误
这个错误我碰到过好多次,大多是因为运行环境的问题——官方文档的示例默认假设你在普通Python脚本环境里跑,但如果你是在Jupyter Notebook、IPython交互式控制台,甚至某些GUI应用里运行,就容易踩这个坑。
为什么会报错?
简单说:Python不允许同一个线程里同时运行多个事件循环。那些交互式环境会在你启动的时候就悄悄帮你启动了一个事件循环,当你再调用asyncio.run(main())时,就会触发"循环已在运行"的错误。而普通脚本里没有预启动的循环,所以官方示例能正常跑。
直接能用的解决方案
场景1:在Jupyter/IPython里运行
直接把asyncio.run(main())换成await main()就行,因为环境已经有现成的循环了,直接await你的主协程就好。修改后的完整代码:
import aiohttp import asyncio import async_timeout async def fetch(session, url): async with async_timeout.timeout(10): async with session.get(url) as response: return await response.text() async def main(): async with aiohttp.ClientSession() as session: html = await fetch(session, 'https://example.com') print(html[:50]) # 替换原来的asyncio.run(main()) await main()
场景2:需要兼容多种环境
如果你的代码要同时在普通脚本和交互式环境里跑,可以写个兼容逻辑,自动判断是否已有运行的循环:
# 上面的fetch和main函数保持不变 try: # 先尝试获取当前正在运行的循环 loop = asyncio.get_running_loop() except RuntimeError: # 没有运行的循环,就设为None loop = None if loop and loop.is_running(): # 复用已有循环,创建任务 task = loop.create_task(main()) # 如果需要等待结果,直接await task就行(仅限支持await的环境) # await task else: # 普通脚本环境,用官方推荐的asyncio.run asyncio.run(main())
场景3:自己写代码时嵌套了循环
如果你是在一个async函数里又调用了asyncio.run()(比如在outer协程里跑inner协程时用了run),那肯定会报错。这时候直接把asyncio.run(inner())改成await inner()就好,不要嵌套启动循环。
错误写法:
async def inner(): await asyncio.sleep(1) async def outer(): # 错!已经在循环里了还跑asyncio.run asyncio.run(inner())
正确写法:
async def inner(): await asyncio.sleep(1) async def outer(): # 直接await内部协程 await inner()
最后总结
90%的情况都是交互式环境的预启动循环导致的,换await main()就能解决。如果是其他情况,先检查是不是自己嵌套了循环调用,或者环境自带了循环,对应调整就行。
内容的提问来源于stack exchange,提问作者Ryan Baumgartner
相关产品推荐
相关产品推荐

