python协程asyncio的应用,async,await,loop

2024-01-08 03:36

本文主要是介绍python协程asyncio的应用,async,await,loop,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

关于协程,asyncio,async,await,loop的概念,参照上一篇文章可迭代对象,迭代器,生成器,协程-CSDN博客

上一章我们详细的讲解了上述各个名词的概念,但是这些东西实际上该怎么用还不太清除,这一章做一点补充;

总结: 

1:async用于定义协程函数

2:协程函数初始化得到的是协程对象,协程对象不能直接执行;

3:协程对象的执行方式,要么用await执行,但是await只能出现在协程函数里面,所以它不能作为协程的启动方式,要么把协程转化为Task运行,因为Task里面含有send(类似于生成器的next,这里用next其实也行,兼容),同理,await为什么能运行协程也是因为它含有send,因为await就是yield from的平替;

4:有了Task以后,如果直接运行Task.run就是手动驱动协程函数,那么对于同时要运行多个协程,就要手动运行多个Task.run,很费劲,所以引入eventloop,eventloop有两个记录列表,一个是就绪列表_ready(一个双向列表),一个是待定任务列表_schedule(一个小顶堆,保证最小时刻任务在最前面);此时loop用run_forver的无限循环,不停的访问_ready去运行任务,并不停的将待定任务列表中满足条件的任务加入到就绪列表;待任务全部运行完则退出;run_until_complete其实就是run_forever的进一步包装

5:asyncio.wait()和asyncio.gather()主要功能就是把各个协程包装成Task,当然网上说他俩都会运行协程函数拿到结果,但其实不是他俩主动驱动协程函数的,本质也是通过loop来驱动执行的,只不过两者做了一定的包装,因为loop.run_until_complete这些只接受一个变量参数,你要运行多个协程的时候终归要先统一起来,asyncio.wait()本身也是一个协程,asyncio.gather()不是协程,但好像也不是普通函数,它继承自future,暂定为一个future吧;

6:asyncio.run()其实是对loop.run_until_complete()的包装,也就是说asyncio.run(协程a)等同于run_until_complete(协程a)

那么整个协程的工作原理即,每次loop运行一个协程函数A,如果在这个协程函数A里面遇到await,则需要等待该await后面跟的协程函数B执行完成,但是B的运行可能需要满足一定的条件,比如B是一个IO,需要等待输入之类的,所以A就被loop挂起,让出当前线程的执行权,loop转而去执行其他协程函数比如C,以此实现单线程多任务并发;

 一:async用于定义协程函数

也就是说,在函数定义前面加上async,则讲该函数变成了一个协程coroutine,此时你是无法直接运行该函数的,因为通过上一章我们知道此时的函数有点类似于定义了yield的函数,定义了yield的函数你第一次初始化的时候也是不运行的,而是返回一个generator生成器,要运行需要用next()和或者send(),那么coroutine协程本身也是从yield发展过来的,所以它也不能直接运行,但此时不再是用next和send了,虽然本质上没啥区别,这里要运行协程函数可以用asyncio.run(协程函数名);

二:Task和Future

上一章我们把本来的三个普通函数全部变成了协程函数async,但其实除此之外,还添加了两个小组件,一个叫做Future(上一章图里最下方的小橙方块),一个叫做Task(继承自Future,上一章图里最上方的小蓝方块),Task的作用就是调控整个协程函数,那么对应的我们要运行协程函数,则必须创建Task任务,因为Task函数里面才有send函数,如图:

这和上一章里面构建的Task样例几乎一样,只不过样例是极简的,并且样例里面设置的是run函数,这里是_step函数; 

也就是说,所有的协程函数,必须转化为Task才可以运行(除了用await方式运行的协程,因为await,相当于yield from,它自己带有send功能),那么我们这里总结一下有多少种运行方式:

a:await

最常用的await关键字可以直接运行协程函数,但是await关键字只能在协程函数里面实现,所以它不能作为协程启动的方式;

b:asyncio.run(协程函数名)

上面的asyncio.run(协程函数名),其实run里面也做了转换操作,把协程转成了Task,asyncio.run里面实际上是调用的loop.run_until_complete来执行函数,如图:

再深入进去, run_until_complete里面做了转换操作,Task继承自Future,这里就将协程转化为了Task:

c: asyncio.wait()

参考自:剖析asyncio.wait的使用_笔记大全_设计学院 (python100.com)

asyncio.wait函数的作用是等待一组协程(coroutine)。当一组协程全部完成后,wait()方法返回两个Set对象:一个是完成的任务列表,一个是未完成的任务列表。

wait()方法常用的参数包括:

1. coros:协程对象的集合。

2. loop=None:事件主循环对象,若未指定默认为asyncio.get_event_loop()。

wait()方法返回的是一个协程

来看一下wait方法的源码:

 

可以看到这里也是把传给它的协程转化为了Task;

但有一点需要注意,asyncio.wait()本身就是一个协程函数,所以它没办法直接asyncio.wait([协程1, 协程2])这样运行的,他一般需要跟在await关键字后面,或者给到loop.run_until_complete等eventloop里面执行

d: asyncio.gather()

其实和asyncio.wait()非常相似,也是运行一组协程函数;但不同点在于

(1):asyncio.wait()用一个set保存所有协程函数,因为set是无序的,所以写成函数也是无序执行的;而gather是顺序执行,顺序输出的;

(2):asyncio.wait()会返回两个值:done 和 pending,done 为已完成的协程 Task,pending 为超时未完成的协程 Task,需通过 future.result 调用 Task 的 result;而asyncio.gather 返回的是所有已完成 Task 的 result,不需要再进行调用或其他操作,就可以得到全部结果;此外,wait()可以在任何一个协程完成时进行一些处理的场景,因为它可以设置任何一个完成时返回:

参考:asyncio.gather vs asyncio.wait_asyncio gather 为什么要加*-CSDN博客

(3):gather不是协程函数,但具体是啥也不清楚,显示如下:

这个类继承自future,姑且当作是一个future吧; gather函数虽然不是协程函数,但仍然无法直接执行,目前来看能驱动它的只有await,或者loop.run_until_complete等eventloop里面驱动;

3:loop

回忆上一章的内容,我们最开始是直接用Task来执行协程的,是手动进行驱动,后面我们引入了eventloop,让他来全程调度协程(其实就是一个无限循环,循环里面不停的运行各个回调函数,这里的回调函数可以是协程函数,也可以是普通函数);

回忆一下eventloop的作用:它有两个列表,一个是就绪列表_ready,一个是待定列表_scheduled,然后每次调用loop.run_forever,或者loop.call_soon,或者loop.call_later来不断执行_ready里面的就绪任务,并且不断判断待定列表里面的任务是否已经就绪,如果就绪,就加入到就绪任务列表执行;所以我们现在准备说的loop.run_until_complete,其实和上面的run_forever,call_soon,call_later这些是一样的,就是用来执行_ready任务列表的,甚至其实run_until_complete就是对run_forever的进一步包装;

所以上面的asyncio.wait()也好,或者asyncio.gather也好,都可以丢给loop来自动调配,也就实现了多任务协作的功能;

我们拿loop.run_until_complete来举例子吧:

loop.run_until_complete可以直接接协程函数,也可以接asyncio.wait()或者asyncio.gather这些,因为本质上loop.run_until_complete里面会把输入强制转化为Task(对于普通函数的话,可能是添加__await__方法,个人理解):

然后就是我们上一章讲过的死循环run_forever和单词循环运行_ready里面准备好的函数:

 

run_once里面的逻辑和上一章我们的极简版本是一样的:

先判断当前已经就绪的任务有多少,然后一个个pop出来,由于这里的任务已经转化为了Task,所以默认调用Task的run方法;整体来看,还是非常清晰明了的;

但是这里存在一个bug,就是我们没看见把任务加入到_ready就绪列表里面的步骤,所以我又回过头去调试看了一下,发现了一点新的亮点:

 asyncio.wait和asyncio.gather进一步的区别:

由于asyncio.wait本质是一个协程函数,所以

loop.run_until_complete(asyncio.wait([ceshi(), ceshi2()]))

这段代码里面asyncio.wait([ceshi(), ceshi2()])返回一个协程对象,并不会先执行asyncio.wait,而是直接执行loop.run_until_complete,在loop.run_until_complete里面把这个协程对象转化为Task的时候,也把这个任务加入到了_ready就绪任务里面:

此处进去后是loop.create_task:

再进去:

 在这里,把协程转化为了Task,并且加入到了_ready就绪列表;然后进入run_forever执行;

但是asyncio.gather不是协程函数,所以,loop.run_until_complete(asyncio.gather(ceshi(), ceshi2()))其实是先进入asyncio.gather函数,再进入run_until_complete,而且gather函数里面就把协程对象转化为了Task,并加入到了loop的_ready就绪列表,调试如下(每一层往下):

 

在这个_ensure_future里面,第一次的时候,没有传入loop,所以先调用了events._get_event_loop(stacklevel=4)去取现有的loop,也就是再最上层我们初始化的loop: 

所以这里用loop.create_task把协程对象转化为Task的时候,就已经加入到_ready就绪列表了 ,后续我们进入到run_until_complete的时候可以发现,此时它的_ready列表已经有了东西:

这篇关于python协程asyncio的应用,async,await,loop的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/582200

相关文章

使用Python将PDF表格自动提取并写入Word文档表格

《使用Python将PDF表格自动提取并写入Word文档表格》在实际办公与数据处理场景中,PDF文件里的表格往往无法直接复制到Word中,本文将介绍如何使用Python从PDF文件中提取表格数据,并将... 目录引言1. 加载 PDF 文件并准备 Word 文档2. 提取 PDF 表格并创建 Word 表格

使用Python实现局域网远程监控电脑屏幕的方法

《使用Python实现局域网远程监控电脑屏幕的方法》文章介绍了两种使用Python在局域网内实现远程监控电脑屏幕的方法,方法一使用mss和socket,方法二使用PyAutoGUI和Flask,每种方... 目录方法一:使用mss和socket实现屏幕共享服务端(被监控端)客户端(监控端)方法二:使用PyA

Python列表的创建与删除的操作指南

《Python列表的创建与删除的操作指南》列表(list)是Python中最常用、最灵活的内置数据结构之一,它支持动态扩容、混合类型、嵌套结构,几乎无处不在,但你真的会创建和删除列表吗,本文给大家介绍... 目录一、前言二、列表的创建方式1. 字面量语法(最常用)2. 使用list()构造器3. 列表推导式

Python使用Matplotlib和Seaborn绘制常用图表的技巧

《Python使用Matplotlib和Seaborn绘制常用图表的技巧》Python作为数据科学领域的明星语言,拥有强大且丰富的可视化库,其中最著名的莫过于Matplotlib和Seaborn,本篇... 目录1. 引言:数据可视化的力量2. 前置知识与环境准备2.1. 必备知识2.2. 安装所需库2.3

Python数据验证神器Pydantic库的使用和实践中的避坑指南

《Python数据验证神器Pydantic库的使用和实践中的避坑指南》Pydantic是一个用于数据验证和设置的库,可以显著简化API接口开发,文章通过一个实际案例,展示了Pydantic如何在生产环... 目录1️⃣ 崩溃时刻:当你的API接口又双叒崩了!2️⃣ 神兵天降:3行代码解决验证难题3️⃣ 深度

Python+FFmpeg实现视频自动化处理的完整指南

《Python+FFmpeg实现视频自动化处理的完整指南》本文总结了一套在Python中使用subprocess.run调用FFmpeg进行视频自动化处理的解决方案,涵盖了跨平台硬件加速、中间素材处理... 目录一、 跨平台硬件加速:统一接口设计1. 核心映射逻辑2. python 实现代码二、 中间素材处

python中的flask_sqlalchemy的使用及示例详解

《python中的flask_sqlalchemy的使用及示例详解》文章主要介绍了在使用SQLAlchemy创建模型实例时,通过元类动态创建实例的方式,并说明了如何在实例化时执行__init__方法,... 目录@orm.reconstructorSQLAlchemy的回滚关联其他模型数据库基本操作将数据添

Python实现快速扫描目标主机的开放端口和服务

《Python实现快速扫描目标主机的开放端口和服务》这篇文章主要为大家详细介绍了如何使用Python编写一个功能强大的端口扫描器脚本,实现快速扫描目标主机的开放端口和服务,感兴趣的小伙伴可以了解下... 目录功能介绍场景应用1. 网络安全审计2. 系统管理维护3. 网络故障排查4. 合规性检查报错处理1.

Python轻松实现Word到Markdown的转换

《Python轻松实现Word到Markdown的转换》在文档管理、内容发布等场景中,将Word转换为Markdown格式是常见需求,本文将介绍如何使用FreeSpire.DocforPython实现... 目录一、工具简介二、核心转换实现1. 基础单文件转换2. 批量转换Word文件三、工具特性分析优点局

Python中4大日志记录库比较的终极PK

《Python中4大日志记录库比较的终极PK》日志记录框架是一种工具,可帮助您标准化应用程序中的日志记录过程,:本文主要介绍Python中4大日志记录库比较的相关资料,文中通过代码介绍的非常详细,... 目录一、logging库1、优点2、缺点二、LogAid库三、Loguru库四、Structlogphp