当前位置: 首页 > news >正文

在FastAPI网站学python:Python 并发 async / await

python并发async / await,结合FastAPI手册进行学习:https://fastapi.tiangolo.com/zh/async/

快速上手async / await

如果正在使用第三方库,它们会告诉你使用 await 关键字来调用它们,就像这样:

results = await some_library()

然后,通过 async def 声明你的 路径操作函数

@app.get('/')
async def read_results():results = await some_library()return results

这时只能在被 async def 创建的函数内使用 await


如果正在使用一个第三方库和某些组件(比如:数据库、API、文件系统...)进行通信,第三方库又不支持使用 await (目前大多数数据库三方库都是这样),这种情况你可以像平常那样使用 def 声明一个路径操作函数,就像这样:

@app.get('/')
def results():results = some_library()return results

如果应用程序不需要与其他任何东西通信而等待其响应,请使用 async def


如果不清楚,使用 def 就好.


注意:可以根据需要在路径操作函数中混合使用 def 和 async def,并使用最适合的方式去定义每个函数。FastAPI 将为他们做正确的事情。

无论如何,在上述任何情况下,FastAPI 仍将异步工作,速度也非常快。

但是,通过遵循上述步骤,它将能够进行一些性能优化。

异步技术细节

Python 的现代版本支持通过一种叫“协程”——使用 async 和 await 语法的东西来写”异步代码“。

让我们在下面的部分中逐一介绍:

  • 异步代码
  • async 和 await
  • 协程

异步代码¶

异步代码仅仅意味着编程语言 💬 有办法告诉计算机/程序 🤖 在代码中的某个点,它 🤖 将不得不等待在某些地方完成一些事情。让我们假设一些事情被称为 "慢文件"📝.

所以,在等待"慢文件"📝完成的这段时间,计算机可以做一些其他工作。

然后计算机/程序 🤖 每次有机会都会回来,因为它又在等待,或者它 🤖 完成了当前所有的工作。而且它 🤖 将查看它等待的所有任务中是否有已经完成的,做它必须做的任何事情。

接下来,它 🤖 完成第一个任务(比如是我们的"慢文件"📝) 并继续与之相关的一切。

这个"等待其他事情"通常指的是一些相对较慢(与处理器和 RAM 存储器的速度相比)的 I/O 操作,比如说:

  • 通过网络发送来自客户端的数据
  • 客户端接收来自网络中的数据
  • 磁盘中要由系统读取并提供给程序的文件的内容
  • 程序提供给系统的要写入磁盘的内容
  • 一个 API 的远程调用
  • 一个数据库操作,直到完成
  • 一个数据库查询,直到返回结果
  • 等等.

这个执行的时间大多是在等待 I/O 操作,因此它们被叫做 "I/O 密集型" 操作。

它被称为"异步"的原因是因为计算机/程序不必与慢任务"同步",去等待任务完成的确切时刻,而在此期间不做任何事情直到能够获取任务结果才继续工作。

相反,作为一个"异步"系统,一旦完成,任务就可以排队等待一段时间(几微秒),等待计算机程序完成它要做的任何事情,然后回来获取结果并继续处理它们。

对于"同步"(与"异步"相反),他们通常也使用"顺序"一词,因为计算机程序在切换到另一个任务之前是按顺序执行所有步骤,即使这些步骤涉及到等待。

 比如在统筹方法一文中,有一个华罗庚烧水泡茶的例子

烧水泡茶方法1 

1、洗水壶:需要1分钟 2、烧开水‌:需要15分钟 3、洗茶壶‌:需要1分钟。4、洗茶杯‌:需要2分钟 5、‌拿茶叶‌:需要1分钟。6、泡茶

这样一共需要20分钟

烧水泡茶方法2 

  1. 洗水壶‌:需要1分钟。
  2. 烧开水‌:需要15分钟。在此期间可以同时进行以下工作:
    • 洗茶壶‌:需要1分钟。
    • 洗茶杯‌:需要2分钟。
    • 拿茶叶‌:需要1分钟。
  3. 泡茶‌:等水开后,泡茶完成。

这样通过统筹安排,总时间可以缩短到16分钟,而不是传统的20分钟。这是华罗庚在统筹方法一文中举的例子。方法2就是异步执行,方法1就是同步、顺序执行

并行处理

如果多个人同时进行泡茶的工作,就是并行处理。我们日常生活中就有很多并行处理的例子,比如银行有多个窗口,火车、地铁有多个车门,植树节大家一起植树,饭店多个炉灶同时做菜等。

现在计算机有多个核心,我们完全可以让它们同时做一些事情,这就是计算机的并行处理。

异步并行

每件事情使用异步处理的方法,但是又同时做多件事情,这就是异步并行。比如前面提到的网络传输、文件读写、数据库存取等操作,既用异步来提高工作效率,也同时做多件来提高效率,这就是异步并行,尤其是在Web服务应用方面,异步并行显著提高系统的吞吐量和响应性!

异步并行,并发 + 并行: Web + 机器学习¶

使用 FastAPI,可以利用 Web 开发中常见的并发机制的优势(NodeJS 的主要吸引力)。

并且,您也可以利用并行和多进程(让多个进程并行运行)的优点来处理与机器学习系统中类似的 CPU 密集型 工作。

这一点,再加上 Python 是“数据科学”、机器学习(尤其是深度学习)的主要语言这一简单事实,使得 FastAPI 与数据科学/机器学习 Web API 和应用程序(以及其他许多应用程序)非常匹配。

了解如何在生产环境中实现这种并行性,可查看此文 Deployment。

async 和 await

现代版本的 Python 有一种非常直观的方式来定义异步代码。这使它看起来就像正常的"顺序"代码,并在适当的时候"等待"。

当有一个操作需要等待才能给出结果,且支持这个新的 Python 特性时,您可以编写如下代码:

burgers = await get_burgers(2)

这里的关键是 await。它告诉 Python 它必须等待 ⏸ get_burgers(2) 完成它的工作 🕙 ,然后将结果存储在 burgers 中。这样,Python 就会知道此时它可以去做其他事情 🔀 ⏯ (比如接收另一个请求)。

要使 await 工作,它必须位于支持这种异步机制的函数内。因此,只需使用 async def 声明它:

async def get_burgers(number: int):# Do some asynchronous stuff to create the burgersreturn burgers

...而不是 def:

# This is not asynchronous
def get_sequential_burgers(number: int):# Do some sequential stuff to create the burgersreturn burgers

使用 async def,Python 就知道在该函数中,它将遇上 await,并且它可以"暂停" ⏸ 执行该函数,直至执行其他操作 🔀 后回来。

当你想调用一个 async def 函数时,你必须"等待"它。因此,这不会起作用:

 
# This won't work, because get_burgers was defined with: async def
burgers = get_burgers(2)


因此,如果您使用的库告诉您可以使用 await 调用它,则需要使用 async def 创建路径操作函数 ,如:

 
@app.get('/burgers')
async def read_burgers():burgers = await get_burgers(2)return burgers

官方的例子需要在fastapi框架内使用才行,这里写了个简单的帮助记忆:

>>> async def read_burgers():
...     burgers = await get_burgers(2)
...     return burgers
... 
>>> def get_burgers(number: int):
...     return number*1234567
... 
>>> y = read_burgers()
>>> print(y)
<coroutine object read_burgers at 0x8be937f4110>

单独异步示例(不包含fastapi代码)

 async 和 await的异步函数

import asyncio  async def say_hello():  print("Hello!")  await asyncio.sleep(1)  # 模拟异步操作,比如 I/O 操作  print("World!")  # 运行异步函数  
asyncio.run(say_hello())

运行结果就是,输出Hello,然后等一秒,再输出world 。在等一秒的时间内,可以做其它异步的事情。

异步并行

import asyncio  async def task(name, duration):  print(f"Task {name} started")  await asyncio.sleep(duration)  print(f"Task {name} sleep {duration} sec completed")  async def main():  # 创建多个异步任务  tasks = [  task("A", 2),  task("B", 1),  task("C", 3)  ]  # 并发执行这些任务  await asyncio.gather(*tasks)  # 运行主异步函数  
asyncio.run(main())

 输出:

>>> asyncio.run(main())
Task A started
Task B started
Task C started
Task B sleep 1 sec completed
Task A sleep 2 sec completed
Task C sleep 3 sec completed

异步上下文管理器

import asyncio  class MyAsyncContext:  async def __aenter__(self):  print("Entering context")  return self  async def __aexit__(self, exc_type, exc_val, exc_tb):  print("Exiting context")  async def main():  async with MyAsyncContext() as ctx:  print("Inside context")  await asyncio.sleep(1)  # 运行主异步函数  
asyncio.run(main())

在这个例子中,MyAsyncContext 是一个自定义的异步上下文管理器,它会在进入和退出上下文时执行异步操作。

输出:

asyncio.run(main())
Entering context
Inside context
Exiting context

异步 I/O 操作

异步编程最常见的应用场景之一是处理 I/O 操作,如网络请求。aiohttp 是一个流行的异步 HTTP 客户端库。

示例:使用 aiohttp 发起异步 HTTP 请求
import aiohttp  
import asyncio  async def fetch(session, url):  async with session.get(url) as response:  return await response.text()  async def main():  async with aiohttp.ClientSession() as session:  html = await fetch(session, 'http://example.com')  print(html[:100])  # 打印响应的前 100 个字符  # 运行主异步函数  
asyncio.run(main())

输出前100个字符:

asyncio.run(main())
<!doctype html>
<html>
<head><title>Example Domain</title><meta charset="utf-8" /><m

 

其它:更多技术细节¶

您可能已经注意到,await 只能在 async def 定义的函数内部使用。

但与此同时,必须"等待"通过 async def 定义的函数。因此,带 async def 的函数也只能在 async def 定义的函数内部调用。

那么,这关于先有鸡还是先有蛋的问题,如何调用第一个 async 函数?

如果您使用 FastAPI,你不必担心这一点,因为"第一个"函数将是你的路径操作函数,FastAPI 将知道如何做正确的事情。

但如果您想在没有 FastAPI 的情况下使用 async / await,则可以这样做。

编写自己的异步代码¶

Starlette (和 FastAPI) 是基于 AnyIO 实现的,这使得它们可以兼容 Python 的标准库 asyncio 和 Trio。

特别是,你可以直接使用 AnyIO 来处理高级的并发用例,这些用例需要在自己的代码中使用更高级的模式。

即使您没有使用 FastAPI,您也可以使用 AnyIO 编写自己的异步程序,使其拥有较高的兼容性并获得一些好处(例如, 结构化并发)。

其他形式的异步代码¶

这种使用 async 和 await 的风格在语言中相对较新。

但它使处理异步代码变得容易很多。

这种相同的语法(或几乎相同)最近也包含在现代版本的 JavaScript 中(在浏览器和 NodeJS 中)。

但在此之前,处理异步代码非常复杂和困难。

在以前版本的 Python,你可以使用多线程或者 Gevent。但代码的理解、调试和思考都要复杂许多。

在以前版本的 NodeJS / 浏览器 JavaScript 中,你会使用"回调",因此也可能导致回调地狱。

协程¶

"协程"只是 async def 函数返回的一个非常奇特的东西的称呼。Python 知道它有点像一个函数,它可以启动,也会在某个时刻结束,而且它可能会在内部暂停 ⏸ ,只要内部有一个 await

通过使用 async 和 await 的异步代码的所有功能大多数被概括为"协程"。它可以与 Go 的主要关键特性 "Goroutines" 相媲美。

 总结¶

让我们再来回顾下上文所说的:

Python 的现代版本可以通过使用 async 和 await 语法创建“协程”,并用于支持“异步代码”。

现在应该能明白其含义了。✨

所有这些使得 FastAPI(通过 Starlette)如此强大,也是它拥有如此令人印象深刻的性能的原因。


http://www.mrgr.cn/news/52713.html

相关文章:

  • C++算法练习-day5——59.螺旋矩阵2
  • MOE论文详解(4)-GLaM
  • 科学家们设计了一种新型胰岛素,能够根据血液中的葡萄糖水平自动开启或关闭
  • 985研一学习日记 - 2024.10.16
  • ClaimsettlementController
  • Linux的开发工具gcc Makefile gdb的学习
  • 新型扩散模型加速框架Hyper-sd分享
  • SQL Injection | SQL 注入 —— 时间盲注
  • 如何正确并优雅的使用Java中的临时文件目录
  • DeBiFormer:带有可变形代理双层路由注意力的视觉Transformer
  • vue + 百度地图GL版实现点聚合
  • C++算法练习-day6——203.移除链表元素
  • flask-socketio-+Nginx反向代理在消息收发和提醒上在使用
  • Scala的fold
  • 思想实验思维浅谈
  • GEE python: RUSLE土壤侵蚀模型的代码
  • 《深度学习》Dlib、OpenCV 轮廓绘制
  • snmpgetnext使用说明
  • STM32+PWM+DMA驱动WS2812
  • C语言 | Leetcode C语言题解之第491题非递减子序列