asyncio 并发编程(一)
Python2 时代高性能的网络编程主要是 Twisted、Tornado 和 Gevent 这三个库,但是它们的异步代码相互之间既不兼容也不能移植。Gvanrossum 希望在 Python 3 实现一个原生的基于生成器的协程库,其中直接内置了对异步 IO 的支持,这就是 asyncio,它在 Python 3.4 被引入到标准库。
并发对比
asyncio 使用单线程、单个进程的方式切换(通常程序等待读或写数据时就是切换上下文的时机)。
requests + ThreadPoolExecutor
这里采用的是线程池 + 同步方式,requests 仅支持同步方式:
import timeimport requests
from concurrent.futures import ThreadPoolExecutornumbers = range(12)
url = 'http://httpbin.org/get?a={}'def fetch(a):print(a)r = requests.get(url.format(a))return r.json()['args']['a']start = time.time()# 开三个线程
with ThreadPoolExecutor(max_workers=3) as executor:for num, result in zip(numbers, executor.map(fetch, numbers)):print('fetch({})'.format(num, result))print('使用 requests + ThreadPoolExector 总耗时:{}'.format(time.time() - start)) # 5.696202754974365
asyncio + aiohttp
协程+异步,现在的 asyncio,有了很多的模块已经在支持:aiohttp,aiodns,aioredis 等等 https://github.com/aio-libs:
import asyncio
import time
import aiohttpurl = 'http://httpbin.org/get?a={}'
numbers = range(12)async def fetch(a):async with aiohttp.ClientSession() as session:async with session.get(url.format(a)) as resp:data = await resp.json() # 等待结果return data['args']['a']start = time.time()
event_loop = asyncio.get_event_loop() # 新建事件循环
tasks = [fetch(num) for num in numbers] # 添加到任务列表# asyncio.gather() 按顺序搜集异步任务执行的结果
results = event_loop.run_until_complete(asyncio.gather(*tasks)) # 开启事件循环for num, result in zip(numbers, results):print('fetch({}) = {}'.format(num, result))print('使用 asyncio + aiohttp 总耗时:{}'.format(time.time() - start)) # 1.5458192825317383
在想进行协程切换的地方使用 await 关键字,上述 await r.json() 会等待 I/O切换。可以看到协程+异步的方式比同步+多线程方法整整快了三四倍。
asyncio 的基本使用
协程本质上是异步非阻塞技术,它可以用一组少量的线程来实现多个任务,一旦某个任务阻塞,则可能用同一线程继续运行其他任务,避免大量上下文的切换,Python 中使用 asyncio 标准库实现协程。
关于 asyncio 中的一些关键字:
event_loop事件循环:程序开启一个无限循环,将一些函数注册到事件循环中,当满足事件发生时,调用相应的协程函数coroutine协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。task任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含了任务的各种状态future: 代表将来执行或没有执行的任务的结果。它和task上没有本质上的区别async/await关键字:python3.5用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。- 事件循环:一种处理多并发量的机制,我们可以定义事件循环来简化使用轮询方法来监控事件
快速入门
import asyncioasync def foo():print('这是一个协程')if __name__ == '__main__':loop = asyncio.get_event_loop() # 定义 loop 对象,事件循环try:print('开始运行协程')coro = foo()print('进入事件循环')loop.run_until_complete(coro) # 用协程启动事件循环,协程返回,这个方法停止循环,接受一个 future 对象finally:print('关闭事件循环')loop.close()
运行结果:
开始运行协程
进入事件循环
这是一个协程
关闭事件循环
协程返回值
import asyncioasync def foo():print('这是一个协程')return '返回值'if __name__ == '__main__':loop = asyncio.get_event_loop()try:print('开始运行协程')coro = foo()print('进入事件循环')result = loop.run_until_complete(coro)print('协程返回值:', result)finally:print('关闭事件循环')loop.close()
Tips:
run_until_complete可以获取协程返回值,若没有,则返回 None
协程调用协程
一个协程可以启动另一个协程,从而可以任务根据工作内容,封装到不同的协程中。我们可以在协程中使用await关键字,链式的调度协程,来形成一个协程任务流:
import asyncioasync def foo():print('开始运行协程,主协程')print('等待 result1 协程运行')res1 = await result1()print('等待 result2 协程运行')res2 = await result2('rose')return res1, res2async def result1():print('result1 协程')return 'result1'async def result2(name):print('result2 协程')return 'result2 协程接收了一个参数:', nameif __name__ == '__main__':loop = asyncio.get_event_loop()try:result = loop.run_until_complete(foo())print('返回值:', result)finally:print('关闭事件循环')loop.close()
运行结果:
开始运行协程,主协程
等待 result1 协程运行
result1 协程
等待 result2 协程运行
result2 协程
返回值: ('result1', ('result2 协程接收了一个参数:', 'rose'))
关闭事件循环
协程中调用普通函数
在协程中可以通过一些方法去调用普通的函数。可以使用的关键字有call_soon、call_later、call_at
call_soon
调用立即返回
loop.call_soon(callback, *args, context=None)
大部分的回调函数支持位置参数,而不支持”关键字参数”,如果是想要使用关键字参数,则推荐使用functools.aprtial()对方法进一步包装。可选关键字context允许指定要运行的回调的自定义contextvars.Context。当没有提供上下文时使用当前上下文。在Python 3.7中, asyncio
协程加入了对上下文的支持。使用上下文就可以在一些场景下隐式地传递变量,比如数据库连接session等,而不需要在所有方法调用显示地传递这些变量。
import asyncio
import functoolsdef callback(args, *, kwargs='default'):print('普通函数 callback作为回调函数,获取参数:', args, kwargs)async def main(loop):print('注册 callback')loop.call_soon(callback, 1)loop.call_soon(callback, kwagrs='rose')# wrapped = functools.partial(callback, kwagrs='not default')# loop.call_soon(wrapped, 2)await asyncio.sleep(0.2)if __name__ == '__main__':loop = asyncio.get_event_loop()try:loop.run_until_complete(main(loop))finally:loop.close()
运行结果:
注册 callback
Traceback (most recent call last):
普通函数 callback作为回调函数,获取参数: 1 defaultFile "D:/pycharm resource/Projects/TestDeploy/协程/协程调用普通函数.py", line 21, in <module>loop.run_until_complete(main(loop))File "C:\Python35\Lib\asyncio\base_events.py", line 342, in run_until_completereturn future.result()File "C:\Python35\Lib\asyncio\futures.py", line 274, in resultraise self._exceptionFile "C:\Python35\Lib\asyncio\tasks.py", line 239, in _stepresult = coro.send(value)File "D:/pycharm resource/Projects/TestDeploy/协程/协程调用普通函数.py", line 12, in mainloop.call_soon(callback, kwagrs='rose')
TypeError: call_soon() got an unexpected keyword argument 'kwagrs'
这里用的是 Python3.5,所以 asyncio 没有对上下文的支持
call_later
延时调用一个函数
loop.call_later(delay, callback, *args, context=None) # 事件循环在delay多长时间之后才执行callback函数
import asyncio
from time import ctimedef callback(n):print('普通函数 callback作为回调函数,获取参数:', n)async def main(loop):print('注册 callback')print('起始时间', ctime())loop.call_later(5, callback, 1)print('第一次延迟调用:', ctime())loop.call_later(10, callback, 2)print('第二次延迟调用:', ctime())loop.call_soon(callback, 3)await asyncio.sleep(2)if __name__ == '__main__':loop = asyncio.get_event_loop()try:loop.run_until_complete(main(loop))finally:print('协程结束', ctime())loop.close()
运行结果:
注册 callback
起始时间 Sat Oct 12 10:28:29 2019
第一次延迟调用: Sat Oct 12 10:28:29 2019
第二次延迟调用: Sat Oct 12 10:28:29 2019
普通函数 callback作为回调函数,获取参数: 3
协程结束 Sat Oct 12 10:28:31 2019
总结:
- call_soon会在
call_later之前执行,和它的位置在哪无关 - call_later的第一个参数越小,越先执行。
call_at
loop.call_at(when, callback, *args, context=None)
第一个参数的含义代表的是一个单调时间,它和我们平时说的系统时间有点差异,指的是事件循环内部时间,可以通过loop.time()获取,然后可以在此基础上进行操作。call_later 内部实质是调用 call_at:
import asynciodef call_back(n, loop):print("callback %s 运行时间点 %s" % (n, loop.time()))async def main(loop):now = loop.time()print("当前的内部时间", now)print("循环时间", now)print("注册callback")loop.call_at(now + 0.1, call_back, 1, loop)loop.call_at(now + 0.2, call_back, 2, loop)loop.call_soon(call_back, 3, loop)await asyncio.sleep(1)if __name__ == '__main__':loop = asyncio.get_event_loop()try:print("进入事件循环")loop.run_until_complete(main(loop))finally:print("关闭循环")loop.close()
运行结果:
进入事件循环
当前的内部时间 148978.593
循环时间 148978.593
注册callback
callback 3 运行时间点 148978.593
callback 1 运行时间点 148978.703
callback 2 运行时间点 148978.796
关闭循环
相关文章:
asyncio 并发编程(一)
Python2 时代高性能的网络编程主要是 Twisted、Tornado 和 Gevent 这三个库,但是它们的异步代码相互之间既不兼容也不能移植。Gvanrossum 希望在 Python 3 实现一个原生的基于生成器的协程库,其中直接内置了对异步 IO 的支持,这就是 asyncio&…...
春招冲刺(二):BFC 盒子面试题总结
BFC 盒子面试题总结 Q1:BFC盒子是什么? BFC全称是Block Formatting Context 意思就是块级格式化上下文。 可以把BFC看做一个容器,容器里边的元素不会影响到容器外部的元素。 Q2:如何创建BFC? 根元素:bo…...
Ep_计网面试题-本地IP地址怎么一层层向上转换?
将数据加上报头打包在一起形成新的数据包继续往下一层传递。拆包的时候就是把数据包去掉包头作为新数据传给上一层 视频讲解: https://edu.csdn.net/course/detail/38090 点我进入 面试宝典 很多人不知道面试问什么,或者其他的XXGuide,那里边的太多没用的,也没有源码解析,都…...
MySQL高级三
目录 三、MySQL高级03 3.1 MyCat 3.1.1 MyCat简介 3.1.2 中间件的作用 3.2 安装MyCat 3.3 主从复制 3.3.1 主从复制的原理 3.3.2 主从复制的好处 3.3.3 配置主从复制 三、MySQL高级03 如果虚拟机的磁盘已满,可以对磁盘进行重新分配 参考:虚拟…...
set和map的基本使用
目录 关联式容器 要点分析 键值对 pair介绍 set 模板参数列表: set的构造: 常用接口 操作 multiset map map的构造 插入 make_pair map的迭代器 operator[] multimap multimap中为什么没有重载operator[] 关联式容器 关联式容器也是用…...
已解决pip install wxPython模块安装失败
已解决(pip install wxPython安装失败)error: legacy-instal1-failure Encountered error while trying to install package.wxPython note: This is an issue with the package mentioned above,not pip. hint : See above for output from …...
Linux基础——连接Xshell7
个人简介:云计算网络运维专业人员,了解运维知识,掌握TCP/IP协议,每天分享网络运维知识与技能。座右铭:海不辞水,故能成其大;山不辞石,故能成其高。个人主页:小李会科技的…...
C++——智能指针1
目录 RAII auto_ptr模拟实现 智能指针拷贝问题 唯一指针 shared_ptr(可以拷贝) shared_ptr模拟实现 完整代码 循环引用 weak_ptr模拟实现 定制删除器 shared_ptr定制删除器模拟实现 内存泄漏 RAII RAII(Resource Acquisit…...
[数据集][VOC][目标检测]翻越栏杆翻越防护栏数据集目标检测可用yolo训练-1035张介绍
数据集格式:Pascal VOC格式(不包含分割路径的txt文件和yolo格式的txt文件,仅仅包含jpg图片和对应的xml) 图片数量(jpg文件个数):1035 标注数量(xml文件个数):1035 标注类别数:2 标注类别名称:["fylg","…...
深度学习 | BN层原理浅谈
深度学习 | BN层原理浅谈 文章目录深度学习 | BN层原理浅谈一. 背景二. BN层作用三. 计算原理四. 注意事项为什么BN层一般用在线性层和卷积层的后面,而不是放在激活函数后为什么BN能抑制过拟合(有争议)一. 背景 神经网络在训练时,由于内存限制࿰…...
每日面试题
2022/12/15 如何实现一个IOC容器 1、配置文件配置包扫描路径 2、递归包扫描获取.class文件 3、反射、确定需要交给lOC管理的类4、对需要注入的类进行依赖注入 配置文件中指定需要扫描的包路径 定义一些注解,分别表示访问控制层、业务服务层、数据持久层、依赖注…...
将IDEA的项目托管到gitee
目录1. 在gitee上创建仓库2. 本地创建仓库目录3. 将项目添加到缓冲区4. 将缓冲区的项目添加到本地仓库5. 将本地仓库的项目上传到gitee6. 遇到的问题6.1 问题描述6.2 解决方法7. 相关图示与补充8. 相关参考1. 在gitee上创建仓库 2. 本地创建仓库目录 在IDEA中选择创建 Git 仓…...
父类子类静态代码块、构造代码块、构造方法执行顺序
github:https://github.com/nocoders/java-everything.git 名词解释 静态代码块:java中使用static关键字修饰的代码块,每个代码块只会执行一次,JVM加载类时会执行静态代码块中的代码,静态代码块先于主方法执行。构造代码块&#…...
【C++】开散列实现unordered_map与unordered_set的封装
本文主要介绍unordered_map与unordered_set的封装,此次封装主要用上文所说到的开散列,通过开散列的一些改造来实现unordered_map与unordered_set的封装 文章目录一、模板参数二、string的特化三、正向迭代器四、构造与析构五、[]的实现六、unordered_map的实现七、u…...
华为OD机试真题Python实现【删除指定目录】真题+解题思路+代码(20222023)
删除指定目录 题目 某文件系统中有 N 个目录, 每个目录都一个独一无二的 ID。 每个目录只有一个付目录, 但每个目录下可以有零个或多个子目录, 目录结构呈树状结构。 假设 根目录的 ID 为0,且根目录没有父目录 ID 用唯一的正整数表示,并统一编号 现给定目录 ID 和其付目…...
CSS选择器大全(上)
基础选择器: id选择器:#id{} 类选择器: .class{} 标签选择器: h1{} 复合选择器: 交集选择器:作用:选中同时符合多个条件的元素 语法:选择器1选择器2选择器3选择器n{} 注意ÿ…...
JavaScript 俄罗斯方块 - setTimeout和rAF
本节内容需要有些基础知识,如进程和线程,队列数据结构 一、setTimeout和setInterval 只要使用过JavaScript的朋友,对setTimeout和setInterval应该不会默生,如果光说怎样去使用这个API,并不难,无非就是隔多少毫秒再执行某个函数,把变化的内容封装在函数中,就可以制作出动…...
LeetCode:构造最大二叉树;使用中序和后序数组构造二叉树;使用前序和中序数组遍历二叉树。
构造二叉树最好都是使用前序遍历;中左右的顺序。 654. 最大二叉树 中等 636 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点,其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前缀上 构建…...
nodejs实现jwt
jwt是json web token的简称,本文介绍它的原理,最后后端用nodejs自己实现如何为客户端生成令牌token和校验token 1.为什么需要会话管理 我们用nodejs为前端或者其他服务提供resful接口时,http协议他是一个无状态的协议,有时候我们…...
结构体占用内存大小如何确定?-->结构体字节对齐 | C语言
目录 一、什么是结构体 二、为什么需要结构体 三、结构体的字节对齐 3.1、示例1 3.2、示例2 3.3、示例3 3.4、示例4 3.5、示例5 四、结构体字节对齐总结 一、什么是结构体 结构体是将不同类型的数据按照一定的功能需 求进行整体封装,封装的数据类型与大小均…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...
