RuntimeError:此事件循环已在python

时间:2017-10-19 09:41:32

标签: python python-asyncio

我认为我收到此错误是因为我的代码调用asyncio.get_event_loop().run_until_complete(foo())两次。从foo()开始,第二次从foo()调用的函数开始。我的问题是:为什么这是一个问题?我为什么要关心这个循环是否正在运行?

对这个问题进行了编辑,我认为这些编辑模糊了(有些人更喜欢在不理解规则的情况下遵循规则,因此从标题中删除了#34;非法"字)。不幸的是,这会造成混乱。

我对错误提出并不感到惊讶。我可以追溯到asyncio来源,看看这个图书馆的作者想要这样做,那里并不神秘。令人费解的是,图书馆的作者认为,当循环已经运行时,从事件循环中要求运行某些功能是非法的。

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

  1. 两种功能都没有终止。
  2. 其中一个功能最终终止。
  3. 这两个函数最终都会终止。
  4. 现在,是否有任何明智的行为可以解决所有三种情况?对我而言,显而易见的是,这里可能存在或可能存在多种理智行为。例如:

    1. 没什么特别的,两个函数的执行是交错的,并且它们会像预期的那样永远运行。
    2. 循环不会将控制权返回到第一个run_until_complete()实例之后的代码,直到第二个函数完成为止(因此run_until_complete()之后不会执行任何代码。
    3. 在最后一个函数终止后,循环将控制权返回给第一个调用run_until_complete忽略所有其他调用站点的代码对象。
    4. 现在,我可以理解这种行为可能不是每个人都想要的。但是,由于这个库决定让程序员控制启动/停止事件循环,它还应该满足这些决策的后果。多次启动同一循环是错误的,这使得库代码无法执行此操作,这会降低使用asyncio的库的质量和实用性(例如,aiohttp确实如此)

6 个答案:

答案 0 :(得分:12)

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

既然你有这个问题,我想你可能会误解asyncio的工作方式。请阅读this article - 它并不大,并提供了很好的介绍。

<强> UPD:

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

await coro()  # add coro() to be run by event loop blocking flow here until coro() is finished

或创建任务:

asyncio.ensure_future(coro())  # add coro() to be run by event loop without blocking flow here

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

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

run_until_complete(foo())表示:“添加foo()以通过事件循环运行并运行事件循环,直到foo()未完成”。

答案 1 :(得分:4)

只需在开头添加一堆代码

!pip install nest_asyncio
import nest_asyncio
nest_asyncio.apply()

答案 2 :(得分:3)

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

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

相反,我决定将所有对asyncio.run的呼叫替换为对此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)

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

答案 3 :(得分:1)

我通过使用nest_async解决了问题

pip install nest_asyncio

,然后在文件中添加以下行。

import nest_asyncio
nest_asyncio.apply()

答案 4 :(得分:1)

我将其写下来并不是为了光顾,而是为了说明我们如何处理仅在事件循环运行时仅排队异步函数并同步等待其结果的情况。

run_until_complete不是用于同步运行任意数量的异步函数,而是用于运行整个异步程序的主入口点。从文档中并不能立即看出该约束。

由于像aiohttp这样的库将把它自己的入口点排队作为服务器运行,并使用run_until_completerun_forever阻止循环的同步操作,因此事件循环将已经在运行,您将不会能够在该事件循环上运行独立的同步操作,并在该线程中等待其结果。

这就是说,如果您必须将异步操作从同步上下文中放入正在运行的事件循环中排队,并获得类似于常规函数的结果,那可能是不可能的。最好的选择是在异步操作完成后传递一个同步回调以进行调用。这当然会减慢您的事件循环。

处理这种情况的另一种方法是在您使用的异步http库的启动和清除回调中执行代码。这是您如何完成此操作的sample

答案 5 :(得分:1)

我遇到了同样的问题,我使用 nest_asyncasyncio

解决了这个问题

只需安装软件包:

pip install nest-asyncio

然后添加这些行:

import nest_asyncio
nest_asyncio.apply()

如果它不适用于 nest_asyncio,则尝试 asyncio

import asyncio
asyncio.set_event_loop(asyncio.new_event_loop())