python3异步编程的学习与实践(python异步编程实战)

网友投稿 545 2022-08-26


python3异步编程的学习与实践(python异步编程实战)

本文中会介绍关于Python3中异步编程的相关知识。希望能够帮助大家理解Python3异步编程的特点以及快速上手异步编程。笔者也会将自己的理解在文中进行阐述,这也算是在和大家交流心得的一个过程。若文中有错误的理解和概念,请大家及时纠正;吸纳大家的建议,对于我来说也是很重要的学习过程之一。

1. 概述

Python3中的异步编程主要是使用协程来实现的。协程的出现使得Python能够在编程语言层面上对事件驱动模型进行支持。协程在提升IO利用率的同时又不会增加开发成本。在Python3中,协程通过使用async与await关键字来进行编程。同时,大量的异步方法都封装在Python3自带的asyncio库中。后续的章节中,会针对关键字与asyncio库进行介绍。

2. 关键字 async/await

早些在Python3.4中,是使用asyncio.coroutine装饰器定义协程函数的。当时asyncio规定所有作为协程执行的函数都需要以asyncio.coroutine装饰器进行修饰。而到了Python3.5中新增支持使用async def语法定义协程函数,但是这样定义的协程函数中不能使用yield语句,只允许使用return或await语句返回数据。另外,Python3.5也引入了await语法(只能在async def定义的函数中使用)。虽然await的使用场景与yield from类似,但是await接收的对象不同(yield from 可接收协程或future对象)。await只能接收可等待对象(awaitable object):一种定义了await()方法(返回非协程本身的遍历器)的对象。协程本身也被视作可等待对象(体现在Python语言设计中,就是collections.abc.Coroutine继承了collections.abc.Awaitable抽象类)。使用async/await编写的协程函数形式如下:

async def get_acl_by_source_ip(config, target, logger): # 构造协程函数队列 aws = [asyncio.create_task(client.del_acl_by_source_ip(target)) for client in get_clients(config, logger)] # 并发调用协程 results = await asyncio.gather(*aws, return_exceptions=True) return results

3. asyncio库

本章节将会从并发执行异步操作,事件循环以及一些常用方法来介绍asyncio库。

3.1 事件循环的管理

因为在协程中所有的事务都是由事务驱动的,因此对于事件循环的管理就尤为重要了。asyncio库中早已为我们准备好了相关的管理方法,使用者只需要注意这些方法的使用技巧即可。asyncio在后续的版本中对底层的事件循环方法都进行了二次封装,即官方所描述的asyncio高层级API。其目的在于让使用者更方便的使用asynico来进行异步编程。同时官方也推荐:应该考虑使用asyncio.run()函数而非使用低层级函数来手动创建和关闭事件循环。封装在低层级API中的事件循环方法一般用于自定义事件循环策略,即如果asynico的默认事件循环机制无法满足现有需求,则可以通过调用这些低层级api来编写自己的事件循环逻辑。

Tips: 注意,如果想使用asyncio封装的高层级API,则Python3版本至少要大于3.7。

通常,都会通过在作为入口方法的main函数中使用asyncio.run()来作为所有协程的入口点。例如:

def main(): # 加载配置 logger = get_logger() conf_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) + os.path.normcase('/conf/ecs.yaml') client_config = load_config(conf_path, logger) if client_config['action'] == 'add' and client_config['targets']: # 添加规则 asyncio.run(add_sg_acl(client_config, logger)) elif client_config['action'] == 'delete' and client_config['targets']: # 删除规则 asyncio.run(del_sg_acl(client_config, logger)) else: logger.warning('配置项参数值无效;请输入有效配置') # 查找指定acl if client_config['search_target']: asyncio.run(search(client_config, logger))

3.2 并发执行异步操作

asyincio库的高级方法中有两个支持并行异步的方法: wait和gather。

两种方法的使用场景如下: 当不在意每一个异步操作中的成功与失败时,建议使用gather()方法。可以将return_exceptions设置为True,以便在返回的所有结果中过滤出异常结果。 当需要控制每一个异步的执行时常时,建议使用wait()方法。若封装异步操作的方法本身内部没有控制超时的逻辑,则可以在外侧使用wait()方法来控制每一个异步操作的操作时常。

对于两者的接受的形参而言,gather方法接受多个可等待对象(协程/task/future),wait方法接受一个装有所有异步操作对应的task或者future对象。例如,构造的gather方法的形参时可以进行如下编码:

# 构造协程函数队列 aws = [asyncio.create_task(client.del_acl_by_source_ip(target)) for client in get_clients(config, logger)] # 并发调用协程 results = await asyncio.gather(*aws, return_exceptions=True)

其中,asyncio的create_task方法是用来构造task对象的。由于我们编写的函数一般都不是asyncio的task对象,因此需要使用create_task方法来将自定义函数专为task对象。

对于并发异步编程,笔者这里想提醒大家一点。在封装异步操作的方法时候,要保证其内部的相关操作也是异步方式实现的。若一个异步方法内包含了同步的操作,那这个异步操作的方法实际上还是以同步的方式在运行。举一个例子,如果在一个协程中有发送请求的操作,并且这个操作是使用urllib3来实现的;那么这个协程在并发执行时,并发速度不会达到最大。因为urllib3的相关操作没有使用异步操作实现,相当于是同步操作。而如果使用例如AIOHTTP库的方法来实现请求操作,则此时就能够真正实现异步并发,达到理想的处理速度。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java实现经典游戏打砖块游戏的示例代码
下一篇:spring data jpa查询一个实体类的部分属性方式
相关文章

 发表评论

暂时没有评论,来抢沙发吧~