代码改变世界

谈谈协程

2016-06-17 10:26  轩脉刃  阅读(...)  评论(... 编辑 收藏

谈谈协程

关于协程,网上能看到很多资料。这里再自个梳理一下。

协程展开来说,叫做协作的程序,想表达的意思是,两段程序,能协作地,共用公共资源,来完成两段程序各自的目的,就叫做协程了。

把现在所有的容易混淆的名字罗列出来:并发,并行,进程,线程,协程。说说他们的历史。

并发

首先是并发,并发的概念是很早就有的了。最早的时候机器都是批处理系统,而且是单道批处理系统,这个系统就是很简单的线性逻辑,一个批处理任务进去,完成,然后进行下面一个。但是随着批处理的任务越来越多,我们希望的是能一次性预存很多批处理任务进入内存,然后通过一定的处理规则指定(作业调度算法)来让这些批处理任务划分CPU的时间,这样CPU的状态就像是第一秒执行A任务,第二秒执行B任务,这样看起来,A任务和B任务就像是同时在进行。这个模式,就叫做并发了。

进程

能并发处理多任务的系统就是多道程序系统。但是有个问题是,CPU在多个批处理任务切换的过程中,对于共享变量,堆栈都需要不断地变化和切换。那么这个时候,就引入了进程的概念,进程就是CPU处理的基本单元。每个进程有自己的上下文,堆栈,共享变量。就像一个盒子,把每个批处理任务和对应的资源都分割开了。

并行

下面,随着CPU硬件技术的发展,一台机器上CPU的数量就不止一个了,那么这个时候,两个CPU就可以同时,单独处理两个进程了。注意,这个同时才是真正的同时,那么这个模式,就叫做并行。

线程

线程的概念出现,是由于进程之前的切换需要消耗的资源太多了,很多时候,我们程序的性能都消耗在进程资源切换上了,所以我们希望的是,能不能将进程切换过程中的资源减少?这里需要深入到进程的切换,进程控制块(PCB)是系统为了管理进程设置的一个专门的数据结构,进程切换的时候,把旧的进程的状态存入到PCB,然后再加载装入新的进程的PCB。PCB里面的信息包含有:程序计数器,寄存器,变量,进程资源等信息。进程之间的切换需要把这些都进行上下文切换,最麻烦耗时的就是切换页表了,页表中存储的是数据段和代码段。这里就需要线程的概念,一个进程中有多个线程,同一个进程中的线程共用代码资源,数据资源,内存资源。所以当同一个进程中的线程进行切换的时候,我们只需要切换计数器,寄存器,变量,并不需要进行切换页表操作,这样,基本创建一个线程比创建一个进程速度要快10-100倍。

不管是进程切换还是线程切换,我们都需要从系统调用开始,即切换必须涉及到用户态和内核态的函数调用。那么,就有人思考,我们是不是可以自己实现一个调度逻辑,让线程的逻辑切换只在用户态进行呢?这样,线程的切换开销就更小了,这个就是用户态线程。

协程

下面就说到协程了。不管是怎么个做法,调度算法对于程序来说都是“被动”式的。难免会遇到这种情况,我们的程序还在IO等待中,结果调度算法把CPU的时间还分配回来给我,虽然我可以通过调度算法立即检查并交出CPU,但是这里还是有一个切换的过程。所以就有人思考,是不是可以做成“主动式”的呢?由程序来告诉计算机,我执行到这里了,下面进行等待IO行为了,这个时候就把CPU的控制权交给别人吧。但是,这个实现的基础当然是调度算法是用户态的。所以,协程就是在用户态线程中,两个程序协商好了,通过某种方式协作运营,共享CPU控制权的方法。

一般来说,这个协商的方法通用的关键字就是yield。