Python FastAPI 异步变量共享

Ram*_*war 5 python asynchronous fastapi

如果我有下面的代码,变量将如何service影响端点的异步性质?变量会被共享吗?或者它会在使用时被锁定,从而阻止其他端点访问它,直到当前端点完成为止?

我问上面的问题是假设服务实例是无状态的,即如果我在每个端点中创建一个服务实例,那么这将是等效的。我不愿意这样做,因为我不知道实例化和销毁服务对象和共享服务对象哪个更耗时?

from typing import List, Union
from fastapi import APIRouter, Body, Depends

# Service definition
router = APIRouter()
service = Service()

@router.post("/a", response_model=Union[A, None])
async def x():
    service.start()
    pass

@router.post("/b", response_model=Union[B, None])
async def y():
    service.stop()
    pass
Run Code Online (Sandbox Code Playgroud)

Yag*_*nci 7

\n

变量服务将如何影响端点的异步性质?

\n
\n

首先,如果您service.stop()不是协程,asyncio 不会进行任何上下文切换。

\n

这意味着它将被阻塞

\n

这也意味着你的函数必须是awaitable,它必须是屈服的

\n
\n

或者使用的时候会被锁住吗

\n
\n

如果存在可能的竞争条件,它不会自动锁定。您需要锁定它 (请参阅asyncio.Lock()

\n

但是如果你的代码没有做任何上下文切换。你不需要担心,因为两个协程不能同时执行,并发不是并行

\n

在事件循环(执行协程的地方)中,协程产生一个它想要恢复的事件。事件循环能够等待这些事件发生。但在等待的同时,它还可以等待其他事件,或者可以处理其他事件。这仅在您有协程时才有效,事件通信是由事件循环的屈服控制完成的。

\n

但是当两个事件共享相同的事件时你应该​​做什么List,你需要锁定它。

\n
\n

为了更清楚地说明这一点,假设您的代码是一家餐厅,协程是女服务员,您向女服务员下订单,然后她去找厨师(事件循环

\n

你需要等待,也不能分享酋长,而酋长可以同时做两个汉堡(处理两个不同的事件

\n

当他没有足够大的网格来同时制作两个汉堡包(共享对象)时会发生什么?

\n

当您的订单正在等待时,您需要锁定网格两个让其他客户订购,但您不能共享网格,因为您有一个网格并且您需要说“嘿我需要锁定这里”

\n

但你仍然可以吃沙拉(其他协程不会被阻止)。

\n
\n
\n

那么在这种情况下,如果我在每个中都执行 Service().start() ,会不会更慢?

\n
\n

长话短说

\n

这个问题完全取决于你的start()函数的作用和作用。

\n

好的,但我需要更好地理解 asyncio。

\n

然后假设您有以下代码

\n
async def x():\n    a = await service.start()\n    return a\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  1. 这将为生成变量分配堆栈空间service().start()
  2. \n
  3. 事件循环将执行此语句并跳转到下一条语句\n
      \n
    1. 一旦start()get 执行,它将压入调用堆栈的值
    2. \n
    3. 这将存储堆栈和指令指针。
    4. \n
    5. 然后它将存储从service().start()to产生的变量a,然后它将恢复堆栈和指令指针。
    6. \n
    \n
  4. \n
  5. 当涉及到return a这一点时,会将 a 的值推送到调用堆栈。
  6. \n
  7. 毕竟它会清除堆栈和指令指针。
  8. \n
\n

请注意,我们之所以能够完成这一切,是因为service().start()是一个协程,而yielding不是返回。

\n

乍一看,您可能不太清楚,但正如我提到的,asyncandawait只是声明和管理协程的奇特语法。

\n
import asyncio\n\n@asyncio.coroutine\ndef decorated(x):\n    yield from x \n\nasync def native(x):\n    await x \n
Run Code Online (Sandbox Code Playgroud)\n

但这两个功能是相同的,做完全相同的事情。您可以将yield from一个或多个函数链接在一起。

\n

但要深入理解异步 I/O,我们需要了解它的作用以及底层的作用。

\n

在大多数操作系统中,基本的 API 都可以通过select()poll()系统调用来使用。

\n

这些接口使 API 的用户能够检查是否有任何应注意的传入 I/O。

\n

例如,您的 HTTP 服务器想要检查是否有任何网络数据包已到达,以便为它们提供服务。借助该系统调用,您可以检查这一点。

\n

当我们查看手册页时,select()我们会看到这个描述。

\n
\n

select() 和 pselect() 允许程序监视多个文件 de\xe2\x80\x90\n脚本,等待一个或多个文件描述符为某类 I/O 操作“准备好”(例如,可以输入)。如果可以执行相应的 I/O 操作,则文件描述符被视为准备就绪

\n
\n

这给了你一个非常基本的想法,并且解释了异步 I/O 的本质。

\n

它可以让您检查描述符是否可以读取和写入。

\n

它不会阻塞其他事情,从而使您的代码更具可扩展性。作为奖励,您的代码会变得更快,但这并不是异步 I/O 的实际目的。

\n

所以要收拾一下。

\n

当一切准备就绪时,事件循环就会不断产生通过这样做,它不会阻塞。

\n