Python3的原生协程(Async/Await)和Tornado异步非阻塞
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_113
我们知道在程序在执行 IO 密集型任务的时候,程序会因为等待 IO 而阻塞,而协程作为一种用户态的轻量级线程,可以帮我们解决这个问题。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存,在调度回来的时候,恢复先前保存的寄存器上下文和栈。因此协程能保留上一次调用时的状态,即所有局部状态的一个特定组合
说人话:说白了就是,当协程遇到io操作而阻塞时,立即切换到别的任务,如果操作完成则进行回调返回执行结果,提高了效率,同时这样也可以充分利用 CPU 和其他资源,这就是异步协程的优势,并且协程本质上是个单进程,相对于多进程来说,无需进程间上下文切换的开销,无需原子操作锁定及同步的开销,编程模型也非常简单。
在python2以及python3.3时代,人们使用协程还得基于greenlet或者gevent,greenlet机制的主要思想是:生成器函数或者协程函数中的yield语句挂起函数的执行,直到稍后使用next()或send()操作进行恢复为止。可以使用一个调度器循环在一组生成器函数之间协作多个任务,它的缺点是必须通过安装三方库进行使用,使用时由于封装特性导致性能有一定的流失。
终于在python3.4中,我们迎来了python的原生协程关键字:Async和Await,它们的底层基于生成器函数,使得协程的实现更加方便。
Async 用来声明一个函数为异步函数,异步函数的特点是能在函数执行过程中挂起,去执行其他异步函数,等到挂起条件(假设挂起条件是sleep(5))消失后,也就是5秒到了再回来执行。
Await 用来用来声明程序挂起,比如异步程序执行到某一步时需要等待的时间很长,就将此挂起,去执行其他的异步程序
首先我们先来看一个不使用协程的程序
import time
def job(t):
time.sleep(t)
print('用了%s' % t)
def main():
[job(t) for t in range(1,3)]
start = time.time()
main()
print(time.time()-start)
从运行结果可以看出,我们的 job 是按顺序执行的。必须执行完 job 1 才能开始执行 job 2, job 1 需要 1 秒的执行时间,job 2 需要 2 秒的执行时间,所以总时间是 3 秒多。
如果我们使用协程的方式,job 1 在等待 time.sleep(t) 执行结束的时候,是可以切换到 job 2 执行的。
import time
import asyncio
async def job(t): # 使用 async 关键字将一个函数定义为协程
await asyncio.sleep(t) # 等待 t 秒, 期间切换执行其他任务
print('用了%s秒' % t)
async def main(loop): # 使用 async 关键字将一个函数定义为协程
tasks = [loop.create_task(job(t)) for t in range(1,3)] # 创建任务, 不立即执行
await asyncio.wait(tasks) # 执行并等待所有任务完成
start = time.time()
loop = asyncio.get_event_loop() # 建立 loop
loop.run_until_complete(main(loop)) # 执行 loop
loop.close() # 关闭 loop
print(time.time()-start)
从运行结果可以看出,我们没有等待 job 1 执行结束再开始执行 job 2,而是 job 1 触发 await 的时候切换到了 job 2 。 这时 job 1 和 job 2 同时在执行 await asyncio.sleep(t),所以最终程序的执行时间取决于执行时间最长的那个 job,也就是 job 2 的执行时间:2 秒
由此可见,效率提高非常明显。
同理,在之前一篇文章中:关于Tornado:真实的异步和虚假的异步提到了tornado默认是同步阻塞机制,如果要激活异步非阻塞的特性,需要使用异步写法,在那篇文章我使用的装饰器的形式来声明异步方法,而在这里,我们同样可以使用async和await来进行协程的异步非阻塞任务
import tornado.web
from tornado import gen
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write('index')
async def doing():
await gen.sleep(10) # here are doing some things
return 'Non-Blocking'
class NonBlockingHandler(tornado.web.RequestHandler):
async def get(self):
result = await doing()
self.write(result)
application = tornado.web.Application([
(r"/", IndexHandler),
(r"/nonblocking", NonBlockingHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
可以看到,虽然代码可读性下降了一点,但是性能和效率却实实在在的提升了
原文转载自「刘悦的技术博客」 https://v3u.cn/a_id_113
Python3的原生协程(Async/Await)和Tornado异步非阻塞的更多相关文章
- 运筹帷幄决胜千里,Python3.10原生协程asyncio工业级真实协程异步消费任务调度实践
我们一直都相信这样一种说法:协程是比多线程更高效的一种并发工作方式,它完全由程序本身所控制,也就是在用户态执行,协程避免了像线程切换那样产生的上下文切换,在性能方面得到了很大的提升.毫无疑问,这是颠扑 ...
- 小议Python3的原生协程机制
此文已由作者张耕源授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 在最近发布的 Python 3.5 版本中,官方正式引入了 async/await关键字.在 asyncio ...
- Python 原生协程------asyncio
协程 在python3.5以前,写成的实现都是通过生成器的yield from原理实现的, 这样实现的缺点是代码看起来会很乱,于是3.5版本之后python实现了原生的协程,并且引入了async和aw ...
- 协程,greenlet原生协程库, gevent库
协程简介 协程(coroutine),又称为微线程,纤程,是一种用户级的轻量级线程.协程拥有自己的寄存器上下文和栈. 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来时,恢复之前保存的上下文 ...
- Python的异步编程[0] -> 协程[1] -> 使用协程建立自己的异步非阻塞模型
使用协程建立自己的异步非阻塞模型 接下来例子中,将使用纯粹的Python编码搭建一个异步模型,相当于自己构建的一个asyncio模块,这也许能对asyncio模块底层实现的理解有更大的帮助.主要参考为 ...
- swoole与php协程实现异步非阻塞IO开发
“协程可以在遇到阻塞的时候中断主动让渡资源,调度程序选择其他的协程运行.从而实现非阻塞IO” 然而php是不支持原生协程的,遇到阻塞时如不交由异步进程来执行是没有任何意义的,代码还是同步执行的,如下所 ...
- [转]向facebook学习,通过协程实现mysql查询的异步化
FROM : 通过协程实现mysql查询的异步化 前言 最近学习了赵海平的演讲,了解到facebook的mysql查询可以进行异步化,从而提高性能.由于facebook实现的比较早,他们不得不对php ...
- 用 async/await 来处理异步
昨天看了一篇vue的教程,作者用async/ await来发送异步请求,从服务端获取数据,代码很简洁,同时async/await 已经被标准化,是时候学习一下了. 先说一下async的用法,它作为一个 ...
- 用async/ await来发送异步
昨天看了一篇vue的教程,作者用async/ await来发送异步请求,从服务端获取数据,代码很简洁,同时async/await 已经被标准化,是时候学习一下了. 先说一下async的用法,它作为一个 ...
随机推荐
- netty系列之:使用Jboss Marshalling来序列化java对象
目录 简介 添加JBoss Marshalling依赖 JBoss Marshalling的使用 总结 简介 在JAVA程序中经常会用到序列化的场景,除了JDK自身提供的Serializable之外, ...
- 219. Contains Duplicate II - LeetCode
Question 219. Contains Duplicate II Solution 题目大意:数组中两个相同元素的坐标之差小于给定的k,返回true,否则返回false 思路:用一个map记录每 ...
- CentOS自动同步时间
安装ntpdate yum install ntpdate -y 测试是否正常 ntpdate cn.ntp.org.cn # 正常情况 [root@centos7 www]# ntpdate cn. ...
- 好客租房55-props深入(2props校验)
对于组件来说 props是外来的 无法保证使用者传入什么格式的数据 传入的数据格式不对 可能会导致组件内部报错 关键问题:不知道报错的具体原因 1安装包props-types 2导入props-typ ...
- Spring是如何整合JUnit的?JUnit源码关联延伸阅读
上一篇我们回答了之前在梳理流程时遇到的一些问题,并思考了为什么要这么设计. 本篇是<如何高效阅读源码>专题的第十二篇,通过项目之间的联系来进行扩展阅读,通过项目与项目之间的联系更好的理解项 ...
- SeataAT模式原理
Seata架构 Seata将分布式事务理解为一个全局事务,它由若干个分支事务组成,一个分支事务就是一个满足ACID的本地事务. Seata架构中有三个角色: TC (Transaction Coord ...
- 「文化课 · 校园生活」街舞社演出 & 校园十佳歌手决赛
女孩子跳舞很好看(流鼻血),男孩子跳舞很骚,跳的很有感觉.
- 用STM32玩L298N(正反转、调速)
目录 用STM32玩L298N(正反转.调速) 控制直流电机正反转 使用PWM调速 用STM32玩L298N(正反转.调速) 开发板:STM32F103ZET6(正点原子F103核心板)/STM32F ...
- [学习笔记]使用Docker+Jenkin自动化流水线发布.Net应用
使用Docker容器方案可以快速安全地将项目部署到客户的服务器上,作为公司项目,需要解决两个问题: 1. 需要搭建一个私有的Docker仓库,以便安全的存储镜像 2. 需要一套自动化发布方案,实现代 ...
- c++ 线段树
关于线段树 线段数是一种区间树 可以看出:叶子即为输入的数 假设一个节点为 x ,则其左儿子为 2x 右儿子为 2x+1 操作解析 约定 变量名 意义 input[] 输入的数 t[] 线段树 其中 ...