当我们要完成一个简单的任务的时候,我们通常就是按照事件的先后顺序按照过程编程,同头到尾程序逐行执行,当我们需要实现的功能越复杂对性能要求越高时我们开始慢慢引入了多线程、多进程、协程等概念,多线程和多进程这个比较好理解,但是协程可能平时就见的不多了,本文对Python 协程到底是什么做出简介。以下全是个人理解如有不妥请多多指正。
什么是协程
根据维基百科给出的定义,“协程 是为非抢占式多任务产生子程序的计算机程序组件,协程允许不同入口点在不同位置暂停或开始执行程序”。听上去感觉很难懂,这里举上一个小小的例子,多线程和多进程都是可以理解为有多个CPU在确实的运行相应的程序,程序并没有发生中断,而协程是运行发生中断的,何时发生中断是由用户决定,产生中断后当前的函数停止工作转而去执行另外一个函数,这个对于嵌入式的中断有点这种意思,单片机当中现在正在运行main函数,这个时候外部中断触发了,main暂时停止转而去执行中断函数,这个时候可能会出现一个想法这不在函数里面调用另外一个函数也是实现的相同功能吗,注意这只是说协程表现出来的一种形式,协程是协调个部分代码达到资源最大利用,这才是真正的协程,在协程中要有任务的安排调整。
讲个故事可能来的比较快一点,现在我们要做的一件事那就是安装Ubuntu系统,之前通过问别人我们大概知道了操作流程是先下载镜像文件,然后在进制作启动盘然后吧啦吧啦一系列操作,好现在我们就用一个单线程的人来模拟这个过程,首先我们进入一个操作也就是程序的一个函数,我们先下载镜像,先打开网站之后一顿操作过后就显示下载进度条了,注意这个动作(想象成程序的函数)还没有执行完,因为我们还没有下载完整个镜像,这个时候我们就开始等进度条,一秒过去了没下载完,我们再等等,两秒过去了还没下载完,这个时候我们就开始不耐烦了,我看进度条显示下载还剩余10分钟,我们好像可以干点别的事情,等下载完过后再来操作,我们这个时候就不再等待了下载了,转而去网站上看等会下载完成了怎么制作启动盘,这个时候我们就实现了任务的调度,从下载镜像转到了流量网页。协程就是这样发生在一个可能发生长时间阻塞的地方,我们不是让CPU做无用的等待,而是让CPU在等待的时间干点其他有用的事情,我们手动进行任务切换的过程就是协程。
代码实现
Python的协程经历了很长的发展,由于只是简介,只介绍最常用也是最简单的一种用法
我们编写一个程序模拟刚才的过程
首先采用最我们用延迟模拟这个过程的耗时,最后统计总的耗时时间
import time def download_ubuntu():time.sleep(5)
def look_up_how_to_operate():time.sleep(3)def install_ubuntu():download_ubuntu()look_up_how_to_operate()def main():start = time.time()install_ubuntu()end = time.time()print ('Cost {} seconds'.format(end - start))if __name__ == "__main__":main()
运行这段代码,我们得到下面的运行时间,无疑就是各个操作时间的累加
Cost 8.004846334457397 seconds
下面我们开始优化过程
import asyncio
import time
async def download_ubuntu():await asyncio.sleep(5)async def look_up_how_to_operate():await asyncio.sleep(3)def install_ubuntu():loop = asyncio.get_event_loop()tasks = [download_ubuntu(),look_up_how_to_operate()]loop.run_until_complete(asyncio.wait(tasks))loop.close()def main():start = time.time()install_ubuntu()end = time.time()print ('Cost {} seconds'.format(end - start))if __name__ == "__main__":main()
通过计时我们看到时间明显就缩短了,将其中的3s就优化出来了,是不是很神奇。
Cost 5.0030436515808105 seconds
应该是在Python 3.5之后提出了这种新的async/await
的操作,在以前的操作中协程还是很麻烦的,但是这种提供了很方便的操作,这个例子或许不是特别的合适下一篇博客会在ROS的实践中继续讲解协程。在本文末尾会对代码做出简要的讲解。
总结
总结起来协程就是实现用户自己编写代码级别的任务的调度。发生在我们需要调用其它模块的函数,这个函数是请求其他模块的一个函数,我们对本线程的任务做出相应的调整。
将一个函数声明为async
就表示这个函数是一个可以被中断的异步函数,在产生中断过后去执行其他的函数。await
关键字表示等待这个步骤完成,这个关键字后面跟的必须是一个可以等待的对象不然就会报错。