Python:当已经有一个事件循环正在运行时,从同步方法调用异步代码

问题描述

我正在和FastAPI和uvloop合作,高效地提供睡觉API服务。

我有很多异步代码调用远程资源,如数据库、存储等,这些函数如下所示:

async def _get_remote_resource(key: str) -> Resource:
    # do some async work
    return resource

我正在实现一个到现有抽象基类的接口,其中我需要在同步方法中使用上面的异步函数。我做了一些类似的事情:

class Resource:
     def __str__(self):
         resource = asyncio.run_until_complete(_get_remote_resource(self.key))
         return f"{resource.pk}"

太好了!现在,我在FastAPI中执行一个端点,以使此工作可访问:

@app.get("")
async def get(key):
     return str(Resource(key))

问题是FastAPI已经使用uvloop获取并运行事件循环,然后异步代码失败,因为循环已经在运行。

有没有什么方法可以从类中的同步方法调用异步方法?或者我必须重新考虑代码的结构?


解决方案

运行时错误旨在阻止您尝试执行的操作。run_until_complete是阻塞调用,在异步def内使用它将停止外部事件循环。

最简单的解决方法是通过实际的异步方法公开所需的功能,例如:

class Resource:
    def name(self):
        return loop.run_until_complete(self.name_async())

    async def name_async(self):
        resource = await _get_remote_resource(self.key)
        return f"{resource.pk}"

然后在FastAPI中,您将以原生方式访问API:

@app.get("")
async def get(key):
     return await Resource(key).name_async()

您还可以将__str__(self)定义为返回self.name(),但最好避免这样做,因为像str()这样的基本内容也应该可以从Asyncio内部调用(由于用于日志记录、调试等)。

相关文章