我们知道在程序在执行 IO 密集型任务的时候,程序会因为等待 IO 而阻塞,而协程作为一种用户态的轻量级线程,可以帮我们解决这个问题。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下
顺晟科技
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().开始()可以看到,虽然代码可读性下降了一点,但是性能和效率却实实在在的提升了