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的用法,它作为一个 ...
随机推荐
- 手把手教你 bash中给变量赋值时 ' 和 " 和 ` 和 $() 的使用
1.赋值指令 var='变量内容' var="变量内容" var=`command` var=$(command) var=变量内容 2.格式要求 =两边不能有空白字符 错误示例 ...
- 通过python将阿里云DNS解析作为DDNS使用
通过python将阿里云DNS解析作为DDNS使用 脚本需要Python2.x运行 安装alidns python sdk sudo pip install aliyun-python-sdk-ali ...
- CentOS7软件环境
一.软件环境 1.1 centos7 [root@centos7 ~]# cat /etc/redhat-release CentOS Linux release 7.4.1708 (Core) [r ...
- 693. Binary Number with Alternating Bits - LeetCode
Question 693. Binary Number with Alternating Bits Solution 思路:输入一个整数,它的二进制01交替出现,遍历其二进制字符串,下一个与上一个不等 ...
- 20 HTTP 长连接与短连接
20 HTTP 长连接与短连接 每日一句 纸上得来终觉浅,绝知此事要躬行. 每日一句 Never give up until the fight is over. 永远不要放弃,要一直战斗到最后一秒. ...
- PyTorch - torch.eq、torch.ne、torch.gt、torch.lt、torch.ge、torch.le
PyTorch - torch.eq.torch.ne.torch.gt.torch.lt.torch.ge.torch.le 参考:https://flyfish.blog.csdn.net/art ...
- C#/VB 数据库连接字符串大全
C#/VB 数据库连接字符串大全 https://www.connectionstrings.com/ SQL Server常用 Standard Security Server=myServerAd ...
- python requires模块 https请求 由于TLS协议版本太高导致错误
错误提示 requests.exceptions.SSLError: HTTPSConnectionPool(host='air.cnemc.cn', port=18007): Max retries ...
- 【RocketMQ】MQ消息发送
消息发送 首先来看一个RcoketMQ发送消息的例子: @Service public class MQService { @Autowired DefaultMQProducer defaultMQ ...
- RPA应用场景-银行回单查询
场景概述银行回单查询 所涉系统名称银行网银 人工操作(时间/次) 5 分钟 所涉人工数量 4 操作频率不定时 场景流程 1.收到外派业务员申请查询收入银行回单的邮件: 2.依据邮件中提供的客户信息进入 ...