进程、线程、协程到底是个什么东西?

1,048 阅读5分钟

一、前言

​ 不管是上大学课程《操作系统》,还是找工作面试,还是日常工作大家交流中,都离不开进程和线程,有些同学可能还会听说过协程。那他们到底是什么呢?他们之间有什么关系呢?跟着我一起往下看吧!

二、计算机组成及操作系统

​ 在讲进程之前,我们先回顾一下计算机的组成。

  1. 中央处理器(CPU):是计算机的核心,负责一些运算和控制

    CPU有两大核心部件,ALU(算数逻辑单元)和CU(逻辑控制单元),其中ALU负责一些运算,包括算数运算(加减乘除)、逻辑运算(与或非)及关系运算(大小等于);CU负责各个逻辑部件的协调工作,充当一个指挥官的角色。CPU中也有寄存器,只不过容量极小,负责缓存一些计算的中间结果。

  2. 主存储器:存储数据

    分为RAM和ROM。RAM,随机存储器,掉电数据丢失,就是俗称的内存;ROM,只读存储器,掉电数据不丢失,就是俗称的磁盘。

  3. IO:各种输入输出设备(鼠标、键盘、显示器、网卡、声卡、显卡等)

有了这些计算机的硬件支持,我们就可以做各种各样的事情了。如果我们手动来控制CPU和内存,那我们可能要写各种底层指令,而且肯定会出现各种各样的bug。操作系统在计算机硬件的基础之上,封装了硬件的实现细节,对上层抽象了一层更方便的系统调用指令,这就是操作系统。

在多任务系统中,操作系统接管了所有硬件资源并持有对硬件控制的最高权限。在操作系统中执行的程序,都以进程的方式运行在更低的权限中。所有的硬件资源,由操作系统根据进程的优先级以及进程的运行状况进行统一的调配。

三、进程

​ 有了操作系统,我们就能够在上面实现程序了。比如通过一个QQ来实现聊天功能。那QQ程序怎么启动起来呢,操作系统把QQ程序丢在一个容器里并把它执行起来,而这个容器就是进程。那进程跟程序之间又是什么关系呢?

  • 程序是死的(只是在磁盘中的一堆指令代码),进程是活的(会占用CPU、内存、文件资源、IO等)
  • 进程是程序的运行实例

四、线程

维基百科:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

一个进程有多个线程,拿QQ为例子,需要有一个线程监听键盘的输入并转换为文字,需要有一个线程负责拉取对方发来的消息等。从操作系统的角度来看:

  • 进程是最小的资源管理分配单元
  • 线程是最小的执行单元

无论是进程还是线程都是需要操作系统来控制的。拿线程来举例,线程有多个执行状态:初始化、可运行、运行中、阻塞、销毁 五种状态。这五种状态的转化关系如下:

线程状态转化图

线程的状态转化是由操作系统内核中的TCB(thread control block)线程控制块来改变的,需要耗费一定的CPU资源。

总结一下,进程是一个程序的运行实例,它管理着各种资源;一个进程有多个线程,线程才是具体的执行单元,他们共享着进程中的部分资源,同样有着私有资源(PC程序计数器、执行栈等),线程间默认通过共享内存进行交互,线程间同步通过锁/信号量等进行互斥操作。由于线程切换需要在操作系统在内核/用户态间的切换才能改变状态,导致线程切换本身会非常耗费资源。

五、协程

​ 协程,又称微线程,纤程。英文名Coroutine。比线程更加轻量级,就像一个进程有多个线程一样,一个线程可以有多个协程。

举一个廖雪峰老师给的例子:

def a():
	print 1
	print 2
def b():
	print 'A'
	print 'B'

当我们调用a、b这两个函数时,肯定会先后打印1 2 A B,如果我想打印1 A B 2 或者 1 A 2 B呢? 显然通过多线程时不能控制的。这样我们就可以通过协程来处理。通过程序自身的逻辑来实现流程跳转,相比于线程中断,有点像CPU中断,不需要通过操作系统的介入,效率极高。

python语言本身支持协程,通过yield关键字来实现。

def a():
    print 1
    yield
    print 2
    yield
def b():
    print 'A'
    print 'B'
generator=a()
next(generator)
b()
generator.send('')

该语句就会打印:

1
A
B
2
  1. 通过调用a()生成一个执行器
  2. 通过next触发执行器执行a并打印1
  3. 调用b打印AB
  4. 通过send像执行器发送信号继续执行并打印2

整个流程是由一个线程执行,通过一些语法来控制执行流程,不涉及到锁,不涉及到线程切换,是通过关键字来辅助ab协作工作,所以叫做协程

ES6中引入了for..of..,它本身是一个语法糖,原理是通过引入迭代器(iterator)来实现,而迭代器就是通过协程来实现的。而在ES7中引入了更加直观的async/await`。

java本身不支持协程,但可以通过三方库来实现,例如QuasarAkka等。

下一篇我们通过jsjava不同的实现来进一步了解协程。

参考文献:

  1. wiki 进程
  2. 阮一峰 <<进程与线程的一个简单解释>>
  3. 漫画:什么是协程?
  4. 了解协程
  5. 廖雪峰 协程
  6. 协程与事件循环