18910140161

Python3的原生协程和Tornado异步非阻塞

顺晟科技

2021-06-16 10:48:51

410

我们知道,当一个程序在执行IO密集型任务时,会因为等待IO而被阻塞,而协和作为用户模式下的轻量级线程,可以帮助我们解决这个问题。协处理器有自己的寄存器环境和堆栈。协同调度切换时保存寄存器上下文和栈,调度回来时恢复之前保存的寄存器上下文和栈。因此协和式飞机可以保留最后一次呼叫的状态,即所有当地状态的特定组合

说白了就是协调进程被io操作阻塞时,立刻切换到另一个任务,如果操作完成了,就回调返回执行结果,提高了效率。同时可以充分利用CPU等资源。这就是异步协调过程的优势,协调过程本质上是一个单一的过程。与多进程相比,它不需要进程间上下文切换、原子操作锁定和同步的开销,编程模型也非常简单。

在python2和python3.3的时代,人们不得不使用基于greenlet或gevent的协同学。greenlet机制的主要思想是生成器函数或协同函数中的yield语句挂起函数的执行,直到后面使用next()或send()操作进行恢复。调度器循环可以用来在一组生成器函数之间协作多个任务,但是它的缺点是必须通过安装一个三方库来使用,并且由于封装,它的性能会丢失。

最后,在python3.4中,我们欢迎python自带的协同关键字:Async和Await,其底层基于生成器函数,使得协同的实现更加方便。

Async用于将函数声明为异步函数。异步函数的特点是可以在函数执行过程中暂停执行其他异步函数,在暂停条件(假设暂停条件是sleep(5))消失后,也就是5秒后再回来执行。

Await用于声明一个程序被挂起。例如,如果一个异步程序需要等待很长时间才能执行某个步骤,它将被挂起以执行其他异步程序

首先,让我们看看一个不使用协同学的程序

导入时间

def job(t):

time.sleep(t)

打印('已用% s“% t”)

def main():

[范围(1,3)中t的作业(t)]

start=time.time()

main()

print(time.time()-start)

从运行结果可以看出,我们的工作是按顺序执行的。必须先执行作业1,才能执行作业2。作业1耗时1秒,作业2耗时2秒,因此总时间超过3秒。

如果我们使用协同学的方法,作业1可以在等待time.sleep(t)的执行完成时切换到作业2。

导入时间

进口海关

Async def job(t): #使用Async关键字将函数定义为协同函数

等待asyncio.sleep(t) #等待t秒,在此期间切换到执行其他任务

打印(用了%s秒“% t”)

Async def main(loop): #使用Async关键字将函数定义为协同函数

tasks=[循环。范围(1,3)]中t的Create _ task(job(t))#创建一个任务,不要立即执行它

等待asyncio.wait(tasks) #执行并等待所有任务完成

start=time.time()

Loop=asyncio.get_event_loop() #创建循环

循环。run _直到_ complete (main (loop)) #执行循环

Loop.close() #关闭循环

print(time.time()-start)

从运行结果可以看出,我们没有等待作业1完成执行作业2,而是在作业1触发等待时切换到作业2。此时,作业1和作业2同时执行,等待asyncio.sleep(t),所以最终程序的执行时间取决于执行时间最长的作业,即作业2的执行时间:2秒

所以效率提升非常明显。

同样的,在前面的文章:tornado:的真实异步和虚假异步中提到tornado默认是同步阻塞机制。如果您想激活异步非阻塞特性,您需要使用异步写入。在那篇文章中,我使用decorator表单来声明异步方法。在这里,我们还可以使用异步和等待来执行异步非阻塞协调任务。

导入tornado.web

来自tornado import gen

class index handler(tornado . web . RequestHandler):

def get(self):

self.write('index ')

async def do():

等待gen.sleep(10) #以下是正在做的一些事情

返回"非阻塞"

类非阻塞处理器(龙卷风。web。RequestHandler):

异步def get(self):

结果=等待do()

self.write(结果)

应用=tornado。web。应用([

(r'/',IndexHandler),

(r'/nonblocking ',NonBlockingHandler),

])

if __name__=='__main__':

application.listen(8888)

tornado.ioloop.IOLoop.instance().开始()可以看到,虽然代码可读性下降了一点,但是性能和效率却实实在在的提升了

  • TAG:
我们已经准备好了,你呢?
2024我们与您携手共赢,为您的企业形象保驾护航