RuntimeError:此事件循环已在python中运行

wvx*_*xvw 25 python python-asyncio

我想我得到了这个错误,因为我的代码调用了asyncio.get_event_loop().run_until_complete(foo())两次.从foo()第二次调用函数开始foo().我的问题是:为什么这会成为问题?我为什么要关心这个循环在运行?


对这个问题进行了编辑,我认为这个编辑模糊了(有些人更喜欢遵循规则而不理解它们,因此从标题中删除了"非法"字样).不幸的是,这会造成混乱.

我不会对错误提出这一事实感到惊讶.我可以追溯到asyncio源头,看看这个图书馆的作者想要这样做,那里没有神秘感.令人费解的部分原因在于库的作者认为在循环已经运行时从事件循环中请求运行某些函数是非法的.

我们可以将问题简化为两个这样的调用,通过案例分析,我们将看到这三种可能性:

  1. 两种功能都不会终止.
  2. 其中一个功能最终终止.
  3. 这两个功能最终终止.

现在,是否有任何明智的行为可以解决所有三种情况?对我而言,显而易见的是,这里可能存在或可能存在多种理智行为.例如:

  1. 没有什么特别的,两个函数的执行是交错的,并且它们会像预期的那样永远运行.
  2. 循环不会将控制权返回run_until_complete()到第二个函数完成之后的第一个实例之后的代码(因此不会run_until_complete()执行后面的代码.
  3. 在最后一个函数终止之后,循环将控制权返回给第一个代码对象,该对象调用run_until_complete忽略所有其他调用站点.

现在,我可以理解这种行为可能不是每个人都想要的.但是,由于这个库决定让程序员控制启动/停止事件循环,它还应该满足这些决策的后果.使多次启动相同循环成为错误会使库代码无法执行此操作,从而降低了库利用的质量和实用性asyncio(例如,确实如此aiohttp).

Mik*_*mov 17

事件循环运行 - 是异步程序的入口点.它管理所有协同程序,任务和回调的运行.在运行循环时没有任何意义:在某种程度上,它就像尝试从同一个已经运行的作业执行程序运行作业执行程序.

既然你有这个问题,我想你可能会误解asyncio的工作方式.请阅读这篇文章 - 它不大并且给出了很好的介绍.

UPD:

在此循环已经运行时,添加要由事件循环运行的多个内容绝对没有问题.你可以等待它来做到这一点:

await coro()  # add coro() to be run by event loop blocking flow here until coro() is finished
Run Code Online (Sandbox Code Playgroud)

或创建任务:

asyncio.ensure_future(coro())  # add coro() to be run by event loop without blocking flow here
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,您不需要调用事件循环的方法来使其运行.

事件循环的方法,如run_foreverrun_until_complete- 只是一般启动事件循环的方法.

run_until_complete(foo())表示:"添加foo()由事件循环运行并运行事件循环本身直到foo()未完成".

  • 重新'文章 - 谢谢你.我现在就读它.与从一开始就将并发性融入其设计的任何语言相比,"asyncio"在如此多的层面上是如此糟糕......它似乎无法赎回.一切都很糟糕,设计,实现,文档... Python总体来说是一个不错的语言,但这个添加...我甚至不知道如何描述它. (6认同)
  • 好吧,这篇文章非常肤浅,有几个 hello-world 级别的例子,当应用于更现实的东西时,这些例子并不成立......:/ (5认同)
  • 我什至不知道从哪里开始描述这篇文章所掩盖的所有问题。我认为编写它的人根本不需要任何类型的并发性,只是将其编写为“理论”练习。无论如何,本文或您的答案中都没有解释为什么尝试在同一循环中运行某些东西而其他东西正在运行时会出现问题。您认为这没有意义,因为当您只有一个非常简单的程序,只有一个入口点且没有库时,您将自己限制在一个非常不切实际的情况下。没有像这样有用的程序。 (2认同)
  • 嗯,实际上有一个问题,这是`asyncio` 的设计......问题是程序员不应该开始/停止运行循环。这本质上是个坏主意。现在,由于需要启动循环,因此存在一个问题:谁启动它?库代码可以启动吗?它需要从非异步代码启动,可能多次。如果两个地方都不知道另一个地方并且只想开始循环怎么办?要回答这个问题,您需要解释为什么当两个不同的函数尝试启动 `asyncio` 时会抛出它。只是说明它确实没有帮助的事实 (2认同)
  • 我看不出同步代码不能将工作提交到事件队列并等待响应的任何理论原因。这看起来确实像是 Python 中的一个实现疏忽。不允许重新进入事件循环会产生毒副作用,即任何需要调用异步函数的代码本身都必须是异步的(除非它在另一个线程中运行)。 (2认同)
  • 我认为只要您想将 asyncio 组件引入遗留项目,就需要 Nest_asyncio (或类似的)。Async-await 的实现(使用默认的 eventet 循环)禁止增量重构(将同步与异步宇宙分区)。对 async 函数的一次调用意味着堆栈中位于其上方的 30 个函数需要使用 async-await 进行处理,无论它们是否关心 I/O(例如计时装饰器、记忆装饰器、单元测试代码......都必须)具有异步意识)。 (2认同)

Dia*_*ine 13

一开始就加这一堆代码

!pip install nest_asyncio
import nest_asyncio
nest_asyncio.apply()
Run Code Online (Sandbox Code Playgroud)

  • 引用 Nest_asyncio:“根据设计,asyncio 不允许嵌套其事件​​循环。这提出了一个实际问题:当在事件循环已经运行的环境中时,不可能运行任务并等待结果。尝试这样做将给出错误“RuntimeError:此事件循环已在运行”。...此模块修补 asyncio 以允许嵌套使用 asyncio.run 和loop.run_until_complete。`来源:https://pypi.org/project/nest-asyncio/ (3认同)
  • 我认为这不是一个好建议。没有解释这段代码的作用、为什么它甚至解决了问题以及它可能有什么副作用。 (2认同)

nur*_*tin 11

我写下这个不是为了光顾,而是为了解释我们如何处理在事件循环运行时简单地将异步函数排队并同步等待它们的结果不起作用的情况。

run_until_complete不是用于同步运行任意数量的任意异步函数,而是用于运行整个异步程序的主要入口点。这个限制在文档中并不明显。

由于像 aiohttp 这样的库会将它自己的入口点排队以作为服务器运行并使用run_until_completeor阻止循环的同步操作run_forever,因此事件循环已经在运行,您将无法在该事件循环上运行独立的同步操作并等待它是该线程内的结果。

话虽如此,如果您必须从同步上下文中将异步操作排队到正在运行的事件循环中,并像常规函数一样获得它的结果,那可能是不可能的。最好的办法是传入一个同步回调,以便在异步操作完成后调用。这当然会减慢你的事件循环。

处理这种情况的另一种方法是在您正在使用的异步 http 库的启动和清理回调中执行您的代码。下面是一个示例,说明您可以如何实现这一点。


mou*_*afa 11

我遇到了同样的问题,我使用Nest_asyncasyncio修复了这个问题

解决方案#1

只需安装该软件包:

pip install nest-asyncio
Run Code Online (Sandbox Code Playgroud)

然后添加这些行:

import nest_asyncio
nest_asyncio.apply()
Run Code Online (Sandbox Code Playgroud)

解决方案#2

如果它不能与 Nest_asyncio 一起使用,请尝试asyncio

import asyncio
asyncio.set_event_loop(asyncio.new_event_loop())
Run Code Online (Sandbox Code Playgroud)


小智 10

我通过使用nest_async解决了问题

pip install nest_asyncio
Run Code Online (Sandbox Code Playgroud)

并在文件中添加以下行。

import nest_asyncio
nest_asyncio.apply()
Run Code Online (Sandbox Code Playgroud)

  • 如果这个答案解释了给定的代码片段实际上做了什么,特别是如果你将它与问题中的代码进行对比的话,那就更好了 (58认同)
  • 我认为我们不需要 `__import__('IPython').embed()`。与此相关的示例用法会很有帮助 (6认同)
  • 不要低估这个技巧的稳定性,因为它用于在 Netflix 的 `papermill` 包中大规模运行 Jupyter Notebooks (`papermill.execute_notebook(nest_asyncio=True`) (3认同)
  • @mirekphd 大采用并不意味着大稳定性,只会进一步加剧货物崇拜。 (3认同)
  • 杰出的!@DeanGurvitz上面的代码猴子修补了异步事件循环并允许它重入(当 run_until_complete 已经在堆栈上时,您可以调用 run_until_complete )。 (2认同)

JBS*_*rro 5

使用nest_asyncio对我不起作用,因为然后aiohttp开始抱怨

  • RuntimeError: Timeout context manager should be used inside a task

相反,我决定将所有调用替换为asyncio.run对 this 的调用asyncio_run

def asyncio_run(future, as_task=True):
    """
    A better implementation of `asyncio.run`.

    :param future: A future or task or call of an async method.
    :param as_task: Forces the future to be scheduled as task (needed for e.g. aiohttp).
    """

    try:
        loop = asyncio.get_running_loop()
    except RuntimeError:  # no event loop running:
        loop = asyncio.new_event_loop()
        return loop.run_until_complete(_to_task(future, as_task, loop))
    else:
        nest_asyncio.apply(loop)
        return asyncio.run(_to_task(future, as_task, loop))


def _to_task(future, as_task, loop):
    if not as_task or isinstance(future, Task):
        return future
    return loop.create_task(future)
Run Code Online (Sandbox Code Playgroud)

次要目标是能够将其asyncio.run视为promise.resolve来自 JS 世界或Task.Wait来自 .NET 世界。