Python:当已经有一个事件循环在运行时,从同步方法调用异步代码
我正在使用FastAPI和uvloop以高效的方式为RESTAPI提供服务 我有很多异步代码可以调用远程资源,如数据库、存储器等,这些函数如下所示:Python:当已经有一个事件循环在运行时,从同步方法调用异步代码,python,asynchronous,python-asyncio,fastapi,Python,Asynchronous,Python Asyncio,Fastapi,我正在使用FastAPI和uvloop以高效的方式为RESTAPI提供服务 我有很多异步代码可以调用远程资源,如数据库、存储器等,这些函数如下所示: async def\u get\u remote\u资源(key:str)->资源: #做一些异步工作 返回资源 我正在实现一个现有抽象基类的接口,我需要在同步方法中使用上面的异步函数。我做过类似的事情: 类资源: 定义(自我): resource=asyncio.run_直到_完成(_get_remote_resource(self.key))
async def\u get\u remote\u资源(key:str)->资源:
#做一些异步工作
返回资源
我正在实现一个现有抽象基类的接口,我需要在同步方法中使用上面的异步函数。我做过类似的事情:
类资源:
定义(自我):
resource=asyncio.run_直到_完成(_get_remote_resource(self.key))
返回f“{resource.pk}”
太好了!现在,我在fastapi中创建了一个端点,以使这项工作可访问:
@app.get(“”)
异步def get(密钥):
返回str(资源(键))
问题是FastAPI已经使用uvloop获取并运行事件循环,然后异步代码失败,因为循环已经在运行
有没有办法从类中的同步方法调用异步方法?或者我必须重新考虑代码的结构吗?运行时错误的设计正是为了阻止您尝试执行的操作
run_,直到_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()
您也可以定义
\uu str\uuuuuuuuuuuuuuself(self)
来返回self.name()
,但这是最好避免的,因为像str()
这样的基本内容也应该可以从asyncio内部调用(由于在日志记录、调试等中使用)。我想补充@user4815162342的答案
FastAPI
是一个不同步的框架。我建议坚持几个原则:
- 不要以阻塞方式在同步函数中执行IO操作。异步准备此资源,并已将准备好的数据传递给同步函数(这一原则可以称为同步代码的异步依赖项)
- 如果您仍然需要在同步代码中执行阻塞IO操作,请在单独的线程中执行。并通过
(asyncio
端点,def
与run_in_executor
或ThreadPoolExecutor
后台任务)异步等待该结果def
- 如果需要执行阻塞CPU绑定的操作,则将其执行委托给单独的进程(最简单的方法是使用
或任何任务队列运行\u in\u executor)ProcessPoolExecutor