Python async 异步编程

news/2024/10/23 7:20:51/

同步:循序渐进执行操作(遇到IO等待,CPU就真的等待IO结束继续执行)
异步:是以IO非阻塞的方式进行计算或数据传输(利用一个任务IO等待的时间,去执行一些其他任务,无需等待),以提高系统的效率和吞吐量。异步计算可以允许多个计算任务同时进行,而不需要等待前一个任务完成。异步计算可以通过使用多线程多进程async异步编程框架等方式来实现。https://www.bilibili.com/video/BV1cK4y1E77y

为提升性能,越来越多的框架(pytorch, tornado, fastapi, django等)都在使用异步非阻塞框架,如async和await装饰的函数就是基于协程的异步函数。

常用在IO较频繁的系统中,如:多GPU加载数据模型训练、Tornado web框架、文件下载、网络爬虫等应用。协程能够在IO等待时间就去切换执行其他任务,当IO操作结束后再自动回调,那么就会大大节省资源并提供性能。

1. 协程

协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术,即通过一个线程实现代码块相互切换执行。

协程,就是利用一个任务IO等待的时间,去执行一些其他任务,可以让原来要使用异步(切换)+回调方式写的非人类代码,用看似同步的方式写出来。

在Python中有多种方式可以实现协程,前两种实现方式较为老旧,所以重点关注后面的方式:(async & awiat

  • greenlet,是一个早期第三方模块,用于实现协程代码(Gevent协程就是基于greenlet实现);
  • yield关键字,生成器,借助生成器的特点也可以实现协程代码;
  • asyncio装饰器,在Python3.4中引入的模块用于编写协程代码;
  • async & awiat关键字,在Python3.5中引入的两个关键字,结合asyncio模块可以更方便的编写协程代码。

async & awiat关键字实现方法

async & await关键字在Python3.5版本中正式引入,async 置于函数 def 前代替@asyncio.coroutine 装饰器await 置于需要切换的IO耗时操作前,让代码可以更加简便可读。

import asyncioasync def func1():print(1)await asyncio.sleep(2)  # 耗时操作,遇到IO耗时操作自动切换到tasks中的其他任务print(2)async def func2():print(3)await asyncio.sleep(2)   # 耗时操作,遇到IO耗时操作自动切换到tasks中的其他任务print(4)tasks = [asyncio.ensure_future(func1()),asyncio.ensure_future(func2())
]loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

1 3 2 4

例如:用代码实现下载 url_list 中的图片。

方式一:同步编程实现

# requests库仅支持同步的http网络请求
import requestsdef download_image(url):print("开始下载:",url)# 发送网络请求,下载图片response = requests.get(url)# 图片保存到本地文件file_name = url.rsplit('_')[-1]with open(file_name, mode='wb') as file_object:file_object.write(response.content)
print("下载完成")if __name__ == '__main__':url_list = ['https://www.1.jpg','https://www.2.jpg','https://www.3.jpg']for item in url_list:download_image(item)

输出:按顺序发送请求,请求一次下载一张图片,假如每次下载花费1s,完成任务需要3s 以上。

方式二:基于协程的程实现

# aiohttp 为支持异步编程的http请求库
import aiohttp
import asyncioasync def fetch(session, url):print("发送请求:", url)async with session.get(url, verify_ssl=False) as response:content = await response.content.read()file_name = url.rsplit('_')[-1]with open(file_name, mode='wb') as file_object:file_object.write(content)async def main():async with aiohttp.ClientSession() as session:url_list = ['https://www.1.jpg','https://www.2.jpg','https://www.3.jpg']tasks = [asyncio.create_task(fetch(session, url)) for url in url_list]await asyncio.wait(tasks)if __name__ == '__main__':asyncio.run(main())

输出:一次发送三个下载请求,同时下载,假如每次下载花费1s,完成任务仅需要1s 左右,第一种方法的耗时为第二种的三倍。

2. asyncio异步编程

异步编程-事件循环

事件循环,可以把他当做是一个while(True)循环,这个while循环在周期性的运行并执行一些任务,在特定条件下终止循环。

# 伪代码任务列表 = [ 任务1, 任务2, 任务3,... ]while True:可执行的任务列表,已完成的任务列表 = 去任务列表中检查所有的任务,将'可执行''已完成'的任务返回for 就绪任务 in 已准备就绪的任务列表:执行已就绪的任务for 已完成的任务 in 已完成的任务列表:在任务列表中移除 已完成的任如果 任务列表 中的任务都已完成,则终止循环

在编写程序时候可以通过如下代码来获取和创建事件循环。

# 方式一:
import asyncio# 生成或获取一个事件循环
loop = asyncio.get_event_loop()# 将任务添加到事件循环中
loop.run_until_complete(任务)# 方式二(python3.7及以上版本支持):
asyncio.run( 任务 )

异步编程-快速上手

async 关键字

协程函数:定义函数时候由async关键字装饰的函数 async def 函数名
协程对象:执行协程函数(),得到的协程对象。

# 协程函数
async def func():pass
# 协程对象
result = func()

注意:执行协程函数只会创建协程对象,函数内部代码不会执行。如果想要运行协程函数内部代码,必须要将协程对象交给事件循环来处理。

import asyncio 
async def func():print("执行协程函数内部代码!")
result = func()# 调用方法1:
# loop = asyncio.get_event_loop()
# loop.run_until_complete( result )# 调用方法2:
asyncio.run( result )

await 关键字

await + 可等待的对象(协程对象、Future、Task对象 -> IO等待),遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行。当前协程挂起时,事件循环可以去执行其他协程(任务)。

import asyncioasync def others():print("start")await asyncio.sleep(2)print('end')return '返回值'async def func():print("执行协程函数内部代码")# await等待对象的值得到结果之后再继续向下走response = await others()print("IO请求结束,结果为:", response)asyncio.run( func() )

Task 对象

Task对象的作用是在事件循环中添加多个任务,用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,这样可以让协程加入事件循环中等待被调度执行。

async def module_a():print("start module_a")await asyncio.sleep(2) # 模拟 module_a 的io操作print('end module_a')return 'module_a 完成'async def module_b():print("start module_b")await asyncio.sleep(1) # 模拟 module_a 的io操作print('end module_b')return 'module_b 完成'  task_list = [module_a(),module_b(), 
]done,pending = asyncio.run( asyncio.wait(task_list) )
print(done)

一般来说CPU的耗时运算方式有:

计算密集型的操作:计算密集型任务的特点是要进行大量的计算、逻辑判断,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等。

IO密集型的操作:涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。

异步编程基于协程实现,如果利用协程实现计算密集型操作,因为线程在上下文之间的来回切换总会经历类似于”计算“–>”保存“–>”创建新环境“ 的一系列操作,导致系统的整体性能反而会下降。所以异步编程并不适用于计算密集型的程序。然而在IO密集型操作汇总,协程在IO等待时间就去切换执行其他任务,当IO操作结束后再自动回调,那么就会大大节省资源并提供性能。


http://www.ppmy.cn/news/1016182.html

相关文章

RN实现混合式开发-内嵌html

介绍 React Native WebView是一个用于在React Native应用中嵌入Web内容的组件。它允许你在应用中显示网页、加载HTML字符串、运行JavaScript代码等。 使用 首先,你需要在你的React Native项目中安装React Native WebView库。可以使用以下命令进行安装:…

折半查找、

描述 给定一个已按从大到小排序好的数组和一个数,使用折半查找算法,输出该数在数组中的位置。如果该数不在数组中,则输出“无此数”。 输入 输入为两行,第一行包含多个整数,用空格分隔,表示已排序好的数…

Transformer理论学习

Transformer出自于论文《attention is all you need》。 一些主流的序列模型主要依赖于复杂的循环结构或者CNN,这里面包含了编解码器等。而Transformer主要的结构是基于注意力机制,而且是用多头注意力机制去替换网络中的循环或者CNN(换言之就是transfor…

Unity之ShaderGraph 节点介绍 数学节点

数学 高级Absolute(绝对值)Exponential(幂)Length(长度)Log(对数)Modulo(余数)Negate(相反数)Normalize(标准化矢量&…

Easys Excel的表格导入(读)导出(写)-----java

一,EasyExcel官网: 可以学习一些新知识: EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 二,为什么要使用easyexcle excel的一些优点和缺点 java解析excel的框架有很多 : poi jxl,存在问题:非常的消耗内存, easyexcel 我们…

前端项目打包报错JavaScript heap out of memory(js堆内存不足)

项目打包出现类似以下报错 <--- JS stacktrace --->FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory1: 00007FF646E9B1EF v8::internal::CodeObjectRegistry::~CodeObjectRegistry1235992: 00007FF646E28BA6 v8::internal::Microta…

车云一体化系统基础理论

车云一体化系统基础理论 介绍目标正文 参考文档 介绍 最近在调研车云链路一体化的整套解决方案&#xff0c;涉及分布式消息队列&#xff08;RocketMQ&#xff09;、分布式存储&#xff08;Doris&#xff09;、离线数据处理&#xff08;Spark&#xff09;、用户行为日志分析&am…

实力认证!TDengine 入选 Gartner 中国数据分析与人工智能技术成熟度曲线

近日&#xff0c;国际权威研究机构 Gartner 发布了《2023 年中国数据分析及人工智能技术成熟度曲线》&#xff08;即《Hype Cycle for Data, Analytics and AI in China, 2023》&#xff09;报告&#xff0c;TDengine 成功入选实时数据管理领域代表产品。 作为评估全球新技术成…