Python3的原⽣协程(AsyncAwait)和Tornado异步⾮阻塞
我们知道在程序在执⾏ 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 关键字将⼀个函数定义为协程
await和async使用方法tasks = [ate_task(job(t)) for t in range(1,3)] # 创建任务, 不⽴即执⾏
await asyncio.wait(tasks) # 执⾏并等待所有任务完成
start = time.time()
loop = _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默认是同步阻塞机制,如果要激活异步⾮阻塞的特性,需要使⽤异步写法,在那篇⽂章我使⽤的装饰器的形式来声明异步⽅法,⽽在这⾥,我们同样可以使⽤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()
可以看到,虽然代码可读性下降了⼀点,但是性能和效率却实实在在的提升了
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论