欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

如何在 Python 混合语言中使用同步和异步函数 - 在并发函数中调用同步函数

最编程 2024-06-09 09:58:24
...

在协程函数中直接调用同步函数会阻塞事件循环,从而影响整个程序的性能。我们先来看一个例子:

以下是使用异步 Web 框架 FastAPI 写的一个例子,FastAPI 是比较快,但不正确的操作将会变得很慢。

import time

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    time.sleep(10)
    return {"message": "Hello World"}


@app.get("/health")
async def health():
    return {"status": "ok"}
登录后复制

上面我们写了两个接口,假设 root 接口函数耗时 10 秒,在这 10 秒内访问 health 接口,想一想会发生什么?

Python混合怎么使用同步和异步函数

访问 root 接口(左),立即访问 health 接口(右),health 接口被阻塞,直至 root 接口返回后,health 接口才成功响应。

time.sleep 就是一个「同步」函数,它会阻塞整个事件循环。

如何解决呢?想一想以前的处理方法,如果一个函数会阻塞主线程,那么就再开一个线程让这个阻塞函数单独运行。所以,这里也是同理,开一个线程单独去运行那些阻塞式操作,比如读取文件等。

loop.run_in_executor 方法将同步函数转换为异步非阻塞方式进行处理。具体来说,loop.run_in_executor() 可以将同步函数创建为一个线程进程,并在其中执行该函数,从而避免阻塞事件循环。

官方例子:在线程或者进程池中执行代码。

那么,我们使用 loop.run_in_executor 改写上面例子,如下:

import asyncio
import time

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    loop = asyncio.get_event_loop()

    def do_blocking_work():
        time.sleep(10)
        print("Done blocking work!!")

    await loop.run_in_executor(None, do_blocking_work)
    return {"message": "Hello World"}


@app.get("/health")
async def health():
    return {"status": "ok"}
登录后复制

效果如下:

Python混合怎么使用同步和异步函数

root 接口被阻塞期间,health 依然正常访问互不影响。

注意: 这里都是为了演示,实际在使用 FastAPI 开发时,你可以直接将 async def root 更换成 def root ,也就是将其换成同步接口函数,FastAPI 内部会自动创建线程处理这个同步接口函数。总的来说,FastAPI 内部也是依靠线程去处理同步函数从而避免阻塞主线程(或主线程中的事件循环)。

推荐阅读