Python异步asyncio学习篇(转)
Python异步编程模块asyncio学习
asyncio是Python3.4引⼊的⼀个标准库,直接内置了对异步IO的⽀持。asyncio模块提供了使⽤协程构建并发应⽤的⼯具。它使⽤⼀种单线程单进程的的⽅式实现并发,应⽤的各个部分彼此合作, 可以显⽰的切换任务,⼀般会在程序阻塞I/O操作的时候发⽣上下⽂切换如等待读写⽂件,或者请求⽹络。同时asyncio也⽀持调度代码在将来的某个特定事件运⾏,从⽽⽀持⼀个协程等待另⼀个协程完成,以处理系统信号和识别其他⼀些事件。
异步并发的概念
对于其他的并发模型⼤多数采取的都是线性的⽅式编写。并且依赖于语⾔运⾏时系统或操作系统的底层线程或进程来适当地改变上下⽂,⽽基于asyncio的应⽤要求应⽤代码显⽰的处理上下⽂切换。
asyncio提供的框架以事件循环(event loop)为中⼼,程序开启⼀个⽆限的循环,程序会把⼀些函数注册到事件循环上。当满⾜事件发⽣的时候,调⽤相应的协程函数。
事件循环
事件循环是⼀种处理多并发量的有效⽅式,在中它被描述为「⼀种等待程序分配事件或消息的编程架构」,我们可以定义事件循环来简化使⽤轮询⽅法来监控事件,通俗的说法就是「当A发⽣时,执⾏B」。事件循环利⽤poller对象,使得程序员不⽤控制任务的添加、删除和事件的控制。事件循环使⽤回调⽅法来知道事件的发⽣。它是asyncio提供的「中央处理设备」,⽀持如下操作:
注册、执⾏和取消延迟调⽤(超时)
创建可⽤于多种类型的通信的服务端和客户端的Transports
启动进程以及相关的和外部通信程序的Transports
将耗时函数调⽤委托给⼀个线程池
单线程(进程)的架构也避免的多线程(进程)修改可变状态的锁的问题。
与事件循环交互的应⽤要显⽰地注册将运⾏的代码,让事件循环在资源可⽤时向应⽤代码发出必要的调⽤。如:⼀个套接字再没有更多的数据可以读取,那么服务器会把控制全交给事件循环。
Future
future是⼀个数据结构,表⽰还未完成的⼯作结果。事件循环可以监视Future对象是否完成。从⽽允许应⽤的⼀部分等待另⼀部分完成⼀些⼯作。
Task
task是Future的⼀个⼦类,它知道如何包装和管理⼀个协程的执⾏。任务所需的资源可⽤时,事件循环会调度任务允许,并⽣成⼀个结果,从⽽可以由其他协程消费。
异步⽅法
使⽤asyncio也就意味着你需要⼀直写异步⽅法。
⼀个标准⽅法是这样的:
⽽⼀个异步⽅法:
从外观上看异步⽅法和标准⽅法没什么区别只是前⾯多了个async。
“Async” 是“asynchronous”的简写,为了区别于异步函数,我们称标准函数为同步函数,
从⽤户⾓度异步函数和同步函数有以下区别:
要调⽤异步函数,必须使⽤await关键字。 因此,不要写regular_double(3),⽽是写await async_double(3).
不能在同步函数⾥使⽤await,否则会出错。
句法错误:
但是在异步函数中,await是被允许的:
协程
启动⼀个协程
⼀般异步⽅法被称之为协程(Coroutine)。asyncio事件循环可以通过多种不同的⽅法启动⼀个协程。⼀般对于⼊⼝函数,最简答的⽅法就是使⽤run_until_complete(),并将协程直接传⼊这个⽅法。
输出
这就是最简单的⼀个协程的例⼦,下⾯让我们了解⼀下上⾯的代码.
第⼀步⾸先得到⼀个事件循环的应⽤也就是定义的对象loop。可以使⽤默认的事件循环,也可以实例化⼀个特定的循环类(⽐如uvloop),这⾥使⽤了默认循环run_until_complete(coro)⽅法⽤这个协程启动循环,协程返回时这个⽅法将停⽌循环。
run_until_complete的参数是⼀个futrue对象。当传⼊⼀个协程,其内部会⾃动封装成task,其中task是Future的⼦类。关于task和future后⾯会提到。
从协程中返回值
将上⾯的代码,改写成下⾯代码
run_until_complete可以获取协程的返回值,如果没有给定返回值,则像函数⼀样,默认返回None。
协程调⽤协程
⼀个协程可以启动另⼀个协程,从⽽可以任务根据⼯作内容,封装到不同的协程中。我们可以在协程中使⽤await关键字,链式的调度协程,来形成⼀个协程任务流。向下⾯的例⼦⼀样。
输出
协程中调⽤普通函数
在协程中可以通过⼀些⽅法去调⽤普通的函数。可以使⽤的关键字有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等,⽽不需要在所有⽅法调⽤显⽰地传递这些变量。
下⾯来看⼀下具体的使⽤例⼦。
输出结果
await和async使用方法通过输出结果我们可以发现我们在协程中成功调⽤了⼀个普通函数,顺序的打印了1和2。
有时候我们不想⽴即调⽤⼀个函数,此时我们就可以call_later延时去调⽤⼀个函数了。
call_later
loop.call_later(delay, callback, *args, context=None)
⾸先简单的说⼀下它的含义,就是事件循环在delay多长时间之后才执⾏callback函数.
配合上⾯的call_soon让我们看⼀个⼩例⼦
输出
通过上⾯的输出可以得到如下结果:
1.call_soon会在call_later之前执⾏,和它的位置在哪⽆关
2.call_later的第⼀个参数越⼩,越先执⾏。
call_at
loop.call_at(when, callback, *args, context=None)
call_at第⼀个参数的含义代表的是⼀个单调时间,它和我们平时说的系统时间有点差异,
这⾥的时间指的是事件循环内部时间,可以通过loop.time()获取,然后可以在此基础上进⾏操作。后⾯的参数和前⾯的两个⽅法⼀样。实际上call_later内部就是调⽤的call_at。
输出
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论