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

asyncio 并发编程(一)

Python2 时代高性能的网络编程主要是 TwistedTornadoGevent 这三个库,但是它们的异步代码相互之间既不兼容也不能移植。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 这三个库&#xff0c;但是它们的异步代码相互之间既不兼容也不能移植。Gvanrossum 希望在 Python 3 实现一个原生的基于生成器的协程库&#xff0c;其中直接内置了对异步 IO 的支持&#xff0c;这就是 asyncio&…...

春招冲刺(二):BFC 盒子面试题总结

BFC 盒子面试题总结 Q1&#xff1a;BFC盒子是什么&#xff1f; BFC全称是Block Formatting Context 意思就是块级格式化上下文。 可以把BFC看做一个容器&#xff0c;容器里边的元素不会影响到容器外部的元素。 Q2&#xff1a;如何创建BFC&#xff1f; 根元素&#xff1a;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 如果虚拟机的磁盘已满&#xff0c;可以对磁盘进行重新分配 参考&#xff1a;虚拟…...

set和map的基本使用

目录 关联式容器 要点分析 键值对 pair介绍 set 模板参数列表&#xff1a; set的构造&#xff1a; 常用接口 操作 multiset map map的构造 插入 make_pair map的迭代器 operator[] multimap multimap中为什么没有重载operator[] 关联式容器 关联式容器也是用…...

已解决pip install wxPython模块安装失败

已解决&#xff08;pip install wxPython安装失败&#xff09;error: legacy-instal1-failure Encountered error while trying to install package.wxPython note: This is an issue with the package mentioned above&#xff0c;not pip. hint : See above for output from …...

Linux基础——连接Xshell7

个人简介&#xff1a;云计算网络运维专业人员&#xff0c;了解运维知识&#xff0c;掌握TCP/IP协议&#xff0c;每天分享网络运维知识与技能。座右铭&#xff1a;海不辞水&#xff0c;故能成其大&#xff1b;山不辞石&#xff0c;故能成其高。个人主页&#xff1a;小李会科技的…...

C++——智能指针1

目录 RAII auto_ptr模拟实现 智能指针拷贝问题 唯一指针 shared_ptr&#xff08;可以拷贝&#xff09; shared_ptr模拟实现 完整代码 循环引用 weak_ptr模拟实现 定制删除器 shared_ptr定制删除器模拟实现 内存泄漏 RAII RAII&#xff08;Resource Acquisit…...

[数据集][VOC][目标检测]翻越栏杆翻越防护栏数据集目标检测可用yolo训练-1035张介绍

数据集格式&#xff1a;Pascal VOC格式(不包含分割路径的txt文件和yolo格式的txt文件&#xff0c;仅仅包含jpg图片和对应的xml) 图片数量(jpg文件个数)&#xff1a;1035 标注数量(xml文件个数)&#xff1a;1035 标注类别数&#xff1a;2 标注类别名称:["fylg","…...

深度学习 | BN层原理浅谈

深度学习 | BN层原理浅谈 文章目录深度学习 | BN层原理浅谈一. 背景二. BN层作用三. 计算原理四. 注意事项为什么BN层一般用在线性层和卷积层的后面&#xff0c;而不是放在激活函数后为什么BN能抑制过拟合(有争议)一. 背景 神经网络在训练时&#xff0c;由于内存限制&#xff0…...

每日面试题

2022/12/15 如何实现一个IOC容器 1、配置文件配置包扫描路径 2、递归包扫描获取.class文件 3、反射、确定需要交给lOC管理的类4、对需要注入的类进行依赖注入 配置文件中指定需要扫描的包路径 定义一些注解&#xff0c;分别表示访问控制层、业务服务层、数据持久层、依赖注…...

将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 名词解释 静态代码块&#xff1a;java中使用static关键字修饰的代码块&#xff0c;每个代码块只会执行一次&#xff0c;JVM加载类时会执行静态代码块中的代码&#xff0c;静态代码块先于主方法执行。构造代码块&#…...

【C++】开散列实现unordered_map与unordered_set的封装

本文主要介绍unordered_map与unordered_set的封装&#xff0c;此次封装主要用上文所说到的开散列,通过开散列的一些改造来实现unordered_map与unordered_set的封装 文章目录一、模板参数二、string的特化三、正向迭代器四、构造与析构五、[]的实现六、unordered_map的实现七、u…...

华为OD机试真题Python实现【删除指定目录】真题+解题思路+代码(20222023)

删除指定目录 题目 某文件系统中有 N 个目录, 每个目录都一个独一无二的 ID。 每个目录只有一个付目录, 但每个目录下可以有零个或多个子目录, 目录结构呈树状结构。 假设 根目录的 ID 为0,且根目录没有父目录 ID 用唯一的正整数表示,并统一编号 现给定目录 ID 和其付目…...

CSS选择器大全(上)

基础选择器&#xff1a; id选择器&#xff1a;#id{} 类选择器&#xff1a; .class{} 标签选择器&#xff1a; h1{} 复合选择器&#xff1a; 交集选择器&#xff1a;作用&#xff1a;选中同时符合多个条件的元素 语法&#xff1a;选择器1选择器2选择器3选择器n{} 注意&#xff…...

JavaScript 俄罗斯方块 - setTimeout和rAF

本节内容需要有些基础知识,如进程和线程,队列数据结构 一、setTimeout和setInterval 只要使用过JavaScript的朋友,对setTimeout和setInterval应该不会默生,如果光说怎样去使用这个API,并不难,无非就是隔多少毫秒再执行某个函数,把变化的内容封装在函数中,就可以制作出动…...

LeetCode:构造最大二叉树;使用中序和后序数组构造二叉树;使用前序和中序数组遍历二叉树。

构造二叉树最好都是使用前序遍历&#xff1b;中左右的顺序。 654. 最大二叉树 中等 636 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前缀上 构建…...

nodejs实现jwt

jwt是json web token的简称&#xff0c;本文介绍它的原理&#xff0c;最后后端用nodejs自己实现如何为客户端生成令牌token和校验token 1.为什么需要会话管理 我们用nodejs为前端或者其他服务提供resful接口时&#xff0c;http协议他是一个无状态的协议&#xff0c;有时候我们…...

结构体占用内存大小如何确定?-->结构体字节对齐 | C语言

目录 一、什么是结构体 二、为什么需要结构体 三、结构体的字节对齐 3.1、示例1 3.2、示例2 3.3、示例3 3.4、示例4 3.5、示例5 四、结构体字节对齐总结 一、什么是结构体 结构体是将不同类型的数据按照一定的功能需 求进行整体封装&#xff0c;封装的数据类型与大小均…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

Java数值运算常见陷阱与规避方法

整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

免费数学几何作图web平台

光锐软件免费数学工具&#xff0c;maths,数学制图&#xff0c;数学作图&#xff0c;几何作图&#xff0c;几何&#xff0c;AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分&#xff1a; 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...

Ubuntu Cursor升级成v1.0

0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开&#xff0c;快捷键也不好用&#xff0c;当看到 Cursor 升级后&#xff0c;还是蛮高兴的 1. 下载 Cursor 下载地址&#xff1a;https://www.cursor.com/cn/downloads 点击下载 Linux (x64) &#xff0c;…...

上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式

简介 在我的 QT/C 开发工作中&#xff0c;合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式&#xff1a;工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...