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

news2024/10/5 19:02:46

关于协程,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列表已经有了东西:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1366802.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Dash+Plotly | Web应用开发(1)

本文为https://github.com/CNFeffery/DataScienceStudyNotes的学习笔记,部分源码来源于此仓库。 本期内容主要为基础概念、web布局方法和交互回调。 文章目录 Dash的主要模块Highlightlayoutcallback 惰性交互阻止初次回调忽略回调匹配错误控制部分回调输出不更新获…

计算机毕业设计----SSM场地预订管理系统

项目介绍 本项目分为前后台,前台为普通用户登录,后台为管理员登录; 用户角色包含以下功能: 按分类查看场地,用户登录,查看网站公告,按分类查看器材,查看商品详情,加入购物车,提交订单,查看订单,修改个人信息等功能。 管理员角…

linux安装codeserver实现云端开发

先看图 下载安装包 https://github.com/coder/code-server/releases 找到code-server-版本号-linux-amd64.tar.gz,我这里是code-server-4.16.1-linux-amd64.tar.gz 1、使用acrm用户登录目标服务器 2、切换root用户,创建 vscode 用户,并设…

selenium对于页面改变的定位元素处理办法

在学习selenimu中,总是发现元素定位不到,想了各种办法,最后总结大致有两个原因。 1.等待时间不够,页面还没有完全渲染就进行操作,使用time模块进行等待。 2.换了页面后,发现定位不到元素,因为…

外包做了1个月,技术退步一大半了。。。

先说一下自己的情况,本科生,20年通过校招进入深圳某软件公司,干了接近4年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

新颖度爆表。网络药理学+PPI+分子对接+实验验证

今天给同学们分享一篇生信文章“The convergent application of metabolites from Avena sativa and gut microbiota to ameliorate non-alcoholic fatty liver disease: a network pharmacology study”,这篇文章发表在J Transl Med期刊上,影响因子为7.…

LeetCode-58/709

1.最后一个单词的长度(58) 题目描述: 给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。 思路&…

如何创建容器搭建节点

1.注册Discord账号 https://discord.com/这是登录网址: https://discord.com/ 2.点击startnow注册,用discord注册或者邮箱注册都可,然后登录tickhosting Tick Hosting这是登录网址:Tick Hosting 3.创建servers 4.点击你创建的servers,按照图中步骤进行

Android性能优化系列——内存优化

内存,是Android应用的生命线,一旦在内存上出现问题,轻者内存泄漏造成App卡顿,重者直接crash,因此一个应用保持健壮,要做好内存的使用和优化。网上有很多讲JAVA内存虚拟机的好文章,我就不赘述了。…

嵌入式-C语言-江科大-指针的详解与应用

文章目录 一:计算机存储机制二:定义指针三:指针的操作四:数组与指针五:指针的应用道友:最清晰的脚印,踩在最泥泞的道路上。 推荐视频配合我的笔记使用 [C语言] 指针的详解与应用-理论结合实践&a…

2024年了,难道还不会使用谷歌DevTools么?

我相信您一定对Chrome浏览器非常熟悉,因为它是前端开发者最亲密的伙伴。我们可以使用它查看网络请求、分析网页性能以及调试最新的JavaScript功能。 除此之外,它还提供了许多功能强大但不常见的功能,这些功能可以大大提高我们的开发效率。 让我们来看看。 1. 重新发送XHR…

网络流量分析与故障分析

1.网络流量实时分析 网络监控 也snmp协议 交换机和服务器打开 snmp就ok了 MRTG或者是prgt 用于对网络流量进行实时监测,可以及时了解服务器和交换机的流量,防止因流量过大而导致服务器瘫痪或网络拥塞。 原理 通过snmp监控 是一个…

pyside6 界面美化库的使用

使用qt_material库,在库中进行导入后,直接使用库提供的皮肤即可非常简单 example: # -*- coding: utf-8 -*- # 使用例子 import sys # from PySide6 import QtWidgets # from PySide2 import QtWidgets from PySide6 import QtWidgets from…

CreateDIBSection失败的问题记录

错误记录 [ERROR] (:0, ): QPixmap::fromWinHICON(), failed to GetIconInfo() (操作成功完成。) [ERROR] (:0, ): QPixmap::fromWinHICON(), failed to GetIconInfo() (参数错误。) [ERROR] (:0, ): QPixmap::fromWinHICON(), failed to GetIconInfo() (参数错误。) [ERROR] …

安卓上使用免费的地图OpenStreetMap

前一段使用了微信的地图,非常的好用。但是存在的问题是海外无法使用,出国就不能用了; 其实国内三家:百度,高德,微信都是一样的问题,当涉及到商业使用的时候需要付费; 国外除了谷歌…

每天刷两道题——第十天

1.1和为k的子数组 给你一个整数数组 n u m s nums nums 和一个整数 k k k ,请你统计并返回 该数组中和为 k k k 的子数组的个数 。子数组是数组中元素的连续非空序列。 输入:nums [1,2,3], k 3 输出:2 前缀和 1.2如何使用 前缀和的…

【Linux Shell】1. 简述

文章目录 【 1. Shell 解释器、Shell语言、Shell脚本 】【 2. Shell 环境 】【 3. 一个简单的 Shell 脚本 】3.1 Shell 脚本的编写3.2 Shell 脚本的运行3.2.1 作为可执行程序运行 Shell 脚本3.2.2 作为解释器参数运行 Shell 脚本 【 1. Shell 解释器、Shell语言、Shell脚本 】 …

基于WIFI指纹的室内定位算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1WIFI指纹定位原理 4.2 指纹数据库建立 4.3定位 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 .....................................…

开发小技巧 - 合理使用Visual Studio 2022内置任务列表(TODO)

前言 在开发编码过程中经常会因为各种问题而打断自己的思绪和开发计划,可能会导致本来准备开发或者需要测试的功能到要上线的时候才想起来没有做完。这种情况相信很多同学都遇到过,咱们强大的Visual Studio内置了一个任务列表(TODO&#xff…

使用使用maven后jstl标签库无法使用

创建maven项目后配置了jstl标签库的依赖,但是一直不行,jsp页面还是原样给我输出,然后去网上找了许多办法,类似于配置文件之类的,结果发现对我并没有什么用,还是原样输出 然后就各种查找,发现了一…