进程
按照教科书上的定义,进程是资源管理的最小单位,线程是程序执行的最小单位。在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持多核处理器以及减小(进程/线程)上下文切换开销。
为什么要这样设计,原因就是:程度运行需要计算机各方面的资源,但CPU速度太快了,寄存器勉强能跟上它,其它诸如RAM及其它设备就更别提了,所以,在执行多个任务的时候CPU采取的策略是轮流着来。
进程是对正在运行程序的一个抽象。一个程序运行时,计算机还能同时读取磁盘,并向屏幕或打印机输出正文。在多道程序设计系统中,CPU由一道程序切换至另一道程序,每道程序各运行几十或几百个毫秒。在一瞬间,CPU只能运行一道程序,但在一秒钟里,它能运行多道程序。
如果在同时有三个程序在运行。这三道程序被抽象为三个各自拥有自己控制流程(即每个程序自己的程序计数器)的进程,并且每个进程都独立运行。
当执行一段代码时,除了CPU,其它相关资源或设备诸如显卡、硬盘等都要准备好,然后CPU才能开始工作。这里,除了CPU之外的所有资源就构成了“上下文”。
多程序具体的轮流方法就是:先加载程序A的上下文,然后开始执行A,保存程序A的上下文,调入下一个要执行的程序B的程序上下文,然后开始执行B,保存程序B的上下文…
线程
一个程序里的独立的子任务叫”线程”。线程是进程内部的单一控制序列流。线程中有程序计数器(记录要执行的下一条指令),寄存器(保存当前的工作变量)等。
线程必须在进程中执行。进程用来把资源集中到一起,而线程则是CPU执行的实体。实际上CPU是不断的在线程中切换,因为一般的进程都是单线程的,所以也可以说是在进程中切换。但因为一个进程内可以具有多个同时发生的线程,这种情况就叫:多线程。同一个进程里的多个线程是可以共享资源的。
一个进程至少需要一个线程作为它的指令执行体,进程管理着资源(比如cpu、内存、文件等等),而将线程分配到某个cpu上执行。
如果我们把进程比喻为一个运行在电脑上的软件,那么一个软件的执行不可能是一条逻辑执行的,必定有多个分支和多个程序段,就好比要实现程序A,实际分成 a,b,c等多个块组合而成。那么这里具体的执行就可能变成
- 程序A得到CPU -> CPU加载上下文
- 开始执行程序A的a小段
- 然后执行A的b小段
- 然后再执行A的c小段
- 最后CPU保存A的上下文
这里a,b,c的执行是共享了A的上下文,CPU在执行的时候没有进行上下文切换的。这里的a,b,c就是线程,也就是说线程是共享了进程的上下文环境,的更为细小的CPU时间段。
协程
协程,又称微线程,纤程。英文名Coroutine。
子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。
子程序调用是通过栈实现的,调用顺序是明确的。而协程的调用和子程序不同。协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。假如有如下伪代码:
def A():
print '1'
print '2'
print '3'
def B():
print 'x'
print 'y'
print 'z'
由协程执行,在执行A的过程中,可以随时中断,去执行B,B也可能在执行过程中中断再去执行A,结果可能是:
1
2
x
y
3
z
但是在A中是没有调用B的,看起来A、B的执行有点像多线程,但协程的特点在于是一个线程执行。
因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。而且协程不需要多线程的锁机制,也不存在变量冲突。
因为协程是一个线程执行,如果要利用多核CPU,最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。相当于用协和替换了线程。