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的用法,它作为一个 ...
随机推荐
- 637. Average of Levels in Binary Tree - LeetCode
Question 637. Average of Levels in Binary Tree Solution 思路:定义一个map,层数作为key,value保存每层的元素个数和所有元素的和,遍历这 ...
- 【FineBI】增量数据更新语句
SELECT * FROM t_abike_user WHERE AddUserTime BETWEEN '2016-11-17 10:49:04' AND '2021-07-31 23:59:59'
- Linux下添加MySql组件后报无权限问题解决
Tomcat日志报错如下: Caused by: java.sql.SQLException: Access denied for user 'root'@'localhost' (using pas ...
- go-zero 微服务实战系列(一、开篇)
前言 在社区中经常看到有人问有没有基于 go-zero 的比较完整的项目参考,该类问题本质上是想知道基于 go-zero 的项目的最佳实践.完整的项目应该是一个完整的产品功能,包含产品需求.架构设计. ...
- Acwing 428
可以找到规律,将第 N 项的 N 对应的二进制表示,转换为以 k 为基底的数即可. N=1=1 ----> 1 ----> \(a^b\) N=2=2 ----> 10 ----&g ...
- 【Spring】AOP实现原理(二):Advisor获取
@EnableAspectJAutoProxy @EnableAspectJAutoProxy注解可以用来开启AOP,那么就从@EnableAspectJAutoProxy入手学习一下Spring A ...
- vue 使用npm install安装依赖失败 【问题分析与解决】
1 进入项目根目录,先通过 npm install 命令安装项目所需依赖,再通过 vue ui 命令打开 Vue Cli 提供的图形化界面,选择项目所在文件夹将项目导入. 出现问题 npm insta ...
- 实测:云RDS MySQL性能是自建的1.6倍
1. 摘要 基于之前写的「云厂商 RDS MySQL 怎么选」的文章,为了进一步了解各云厂商在RDS MySQL数据库性能上的差异,本文将对自建MySQL.阿里云.腾讯云.华为云和AWS 的 RDS ...
- 关于webstorm更换主题
现在我们前端使用编辑器,只要用习惯就好,不过这里推荐使用webstorm,因为被称为,'js神器'的称号,不是白说的.接下来我们来看下怎么引入主题. 下面有一个网站,这个网站的名字叫 http://w ...
- 【前端面试】(三)JavaScript相等(==)和全等(===)运算符的区别
视频链接: JavaScript相等()和全等(=)运算符的区别 - Web前端工程师面试题讲解 参考链接: JavaScript == 与 === 区别 区别 对于string.number 等基础 ...