跟随 asyncio.run() 时 asyncio.get_event_loop() 失败

Posted

技术标签:

【中文标题】跟随 asyncio.run() 时 asyncio.get_event_loop() 失败【英文标题】:asyncio.get_event_loop() fails when following asyncio.run() 【发布时间】:2021-12-03 12:37:30 【问题描述】:

我正在尝试找到一种解决方案来在同步上下文中调用异步函数

以下是我的参考资料:

Python call callback after async function is done When using asyncio, how do you allow all running tasks to finish before shutting down the event loop https://docs.python.org/zh-cn/3/library/asyncio-task.html RuntimeError: This event loop is already running in python call async function in main function

但我发现,asyncio.get_event_loop() 在执行 asyncio.run() 时失败,这是我重现此问题的代码:

import asyncio

async def asyncfunction(n):
    print(f'before sleep in asyncfunction( n )')
    await asyncio.sleep(1)
    print(f'after sleep in asyncfunction( n )')
    return f'result of asyncfunction( n )'

def callback(r):
    print(f'inside callback, got: r')

r0 = asyncio.run(asyncfunction(0)) # cause following asyncio.get_event_loop() fail.
callback(r0)
print('sync code following asyncio.run(0)')

r1 = asyncio.run(asyncfunction(1)) # but following asyncio.run() still works.
callback(r1)
print('sync code following asyncio.run(1)')

async def wrapper(n):
    r = await asyncfunction(n)
    callback(r)

asyncio.get_event_loop().create_task(wrapper(2)) #fail if there  is asyncio.run() before
print('sync code following loop.create_task(2)')

#RuntimeError: There is no current event loop in thread 'MainThread'.

asyncio.get_event_loop().create_task(wrapper(3)) #the second call works if there is no asyncio.run() before
print('sync code following loop.create_task(3)')

# main

_all = asyncio.gather(*asyncio.all_tasks(asyncio.get_event_loop()))
asyncio.get_event_loop().run_until_complete(_all)

我认为这可能是因为事件循环被某种东西“消耗”了,asyncio.set_event_loop(asyncio.new_event_loop()) 可能是一种解决方法,但我不确定 这是否是最终用户设置的预期用途手动事件循环。我也想知道这里的为什么一切是如何发生的


看了asyncio.run的部分源码。我知道为什么会这样。

但我仍然想知道在同步上下文中调用异步函数的预期方式是什么

以下代码似乎有效(在每次asyncio.run() 调用后设置一个新的事件循环):

asyncio.run(asyncfunction()) 
asyncio.set_event_loop(asyncio.new_event_loop())

但这有点奇怪,似乎不是预期的方式。

【问题讨论】:

根据asyncio.run 的文档:“这个函数总是创建一个新的事件循环并在最后关闭它。” @dirn 感谢您指出这一点。我现在添加了更多描述和子问题。 看到这个答案:***.com/questions/69710875/… 【参考方案1】:

当您调用asyncio.run 时,它会在您每次调用它时创建一个新的事件循环。当asyncio.run 完成时,该事件循环随后被销毁。因此,在您的第二个 asyncio.run 完成后的代码示例中,根本没有事件循环,您之前创建的两个不再存在。 asyncio.get_event_loop 通常会为您创建一个新的事件循环,除非之前调用了 set_event_loop,而 asyncio.run 确实会这样做(这解释了为什么如果您删除 asyncio.run 事情会起作用)。要修复您的代码,您应该创建一个新的事件循环并使用它而不是调用 get_event_loop,请记住这是第三个循环,可能不是您想要的。

import asyncio

async def asyncfunction(n):
  print(f'before sleep in asyncfunction( n )')
  await asyncio.sleep(1)
  print(f'after sleep in asyncfunction( n )')
  return f'result of asyncfunction( n )'

def callback(r):
  print(f'inside callback, got: r')

r0 = asyncio.run(asyncfunction(0))
callback(r0)
print('sync code following asyncio.run(0)')

r1 = asyncio.run(asyncfunction(1))
callback(r1)
print('sync code following asyncio.run(1)')

async def wrapper(n):
  r = await asyncfunction(n)
  callback(r)

loop = asyncio.new_event_loop()
loop.create_task(wrapper(2))
print('sync code following loop.create_task(2)')


loop.create_task(wrapper(3))
print('sync code following loop.create_task(3)')

# main

_all = asyncio.gather(*asyncio.all_tasks(loop))
loop.run_until_complete(_all)

【讨论】:

感谢您的回答,但您能否指出在 python asyncio 中在同步上下文中调用异步函数的预期方法是什么 @luochen1990 这取决于你的情况。通常你会手动创建一个事件循环并调用loop.run_until_complete(async_function),但是如果你需要它立即返回,那么在你已经完成的情况下创建一个任务是可行的。 你的意思是asyncio.new_event_loop().run_until_complete(async_function()) @luochen1990 如果您希望每次都创建一个新的事件循环,那么可以,但这似乎不需要。您可以使用 loop = asyncio.new_event_loop() 创建一个事件循环,并使用 loop.run_until_complete 在多个调用中重复使用它 来自***.com/a/56662635/1608276,“run_until_complete 不是用于同步运行任意数量的任意异步函数,而是用于运行整个异步程序的主入口点。这个约束在文档中并不明显。”我不确定哪个是正确的.... :(

以上是关于跟随 asyncio.run() 时 asyncio.get_event_loop() 失败的主要内容,如果未能解决你的问题,请参考以下文章

`asyncio.run()` 不等待协程完成

无法从正在运行的事件循环中调用 asyncio.run()

asyncio.run_in_executor 是多线程的吗?

Django — async_to_sync 与 asyncio.run

Python asyncio run_coroutine_threadsafe 从不运行协程?

“GIL”如何影响具有 i/o 绑定任务的 Python asyncio `run_in_executor`?