当循环运行时,如何运行协同例程并等待同步函数产生的结果?

2022-03-25 00:00:00 python python-3.x python-asyncio

问题描述

我有一个类似愚弄的代码:

def render():
    loop = asyncio.get_event_loop()

    async def test():
        await asyncio.sleep(2)
        print("hi")
        return 200

    if loop.is_running():
        result = asyncio.ensure_future(test())
    else:
        result = loop.run_until_complete(test())

loop没有运行时非常简单,只需使用loop.run_until_complete,它会返回coro结果,但是如果循环已经在运行(我的阻塞代码运行在已经在运行循环的应用程序中),我不能使用loop.run_until_complete,因为它会引发异常;当我调用asyncio.ensure_future时,任务会被调度并运行,但我想在那里等待结果,有人知道怎么做吗?文档不太清楚如何执行此操作。

我尝试在coro中传递一个concurrent.futures.Future调用set_result,然后在我的阻塞代码上调用Future.result(),但它不起作用,它在那里阻塞,并且不让任何其他东西运行。如有任何帮助,我们将不胜感激。


解决方案

要使用建议的设计实现runner,您需要一种方法来单步执行在其中运行的回调的事件循环。Asyncioexplicitly forbids递归事件循环,因此此方法是死胡同。

考虑到该约束,您有两个选择:

  1. 使render()本身成为协程;
  2. 在与运行异步事件循环的线程不同的线程中执行render()(及其调用方)。

假设不存在#1,您可以实现render()的#2变体,如下所示:

def render():
    loop = _event_loop  # can't call get_event_loop()

    async def test():
        await asyncio.sleep(2)
        print("hi")
        return 200

    future = asyncio.run_coroutine_threadsafe(test(), loop)
    result = future.result()
请注意,您不能在render中使用asyncio.get_event_loop(),因为没有(也不应该)为该线程设置事件循环。相反,派生运行器线程的代码必须调用asyncio.get_event_loop()并将其发送到线程,或者只是将其保留在全局变量或共享结构中。

相关文章