一、前言
不管是上大学课程《操作系统》,还是找工作面试,还是日常工作大家交流中,都离不开进程和线程,有些同学可能还会听说过协程。那他们到底是什么呢?他们之间有什么关系呢?跟着我一起往下看吧!
二、计算机组成及操作系统
在讲进程之前,我们先回顾一下计算机的组成。
- 中央处理器(CPU):是计算机的核心,负责一些运算和控制
CPU有两大核心部件,ALU(算数逻辑单元)和CU(逻辑控制单元),其中ALU负责一些运算,包括算数运算(加减乘除)、逻辑运算(与或非)及关系运算(大小等于);CU负责各个逻辑部件的协调工作,充当一个指挥官的角色。CPU中也有寄存器,只不过容量极小,负责缓存一些计算的中间结果。
- 主存储器:存储数据
分为RAM和ROM。RAM,随机存储器,掉电数据丢失,就是俗称的内存;ROM,只读存储器,掉电数据不丢失,就是俗称的磁盘。
- IO:各种输入输出设备(鼠标、键盘、显示器、网卡、声卡、显卡等)
有了这些计算机的硬件支持,我们就可以做各种各样的事情了。如果我们手动来控制CPU和内存,那我们可能要写各种底层指令,而且肯定会出现各种各样的bug。操作系统在计算机硬件的基础之上,封装了硬件的实现细节,对上层抽象了一层更方便的系统调用指令,这就是操作系统。
在多任务系统中,操作系统接管了所有硬件资源并持有对硬件控制的最高权限。在操作系统中执行的程序,都以进程的方式运行在更低的权限中。所有的硬件资源,由操作系统根据进程的优先级以及进程的运行状况进行统一的调配。
三、进程
有了操作系统,我们就能够在上面实现程序了。比如通过一个QQ来实现聊天功能。那QQ程序怎么启动起来呢,操作系统把QQ程序丢在一个容器里并把它执行起来,而这个容器就是进程。那进程跟程序之间又是什么关系呢?
- 程序是死的(只是在磁盘中的一堆指令代码),进程是活的(会占用CPU、内存、文件资源、IO等)
- 进程是程序的运行实例
四、线程
维基百科:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
一个进程有多个线程,拿QQ为例子,需要有一个线程监听键盘的输入并转换为文字,需要有一个线程负责拉取对方发来的消息等。从操作系统的角度来看:
- 进程是最小的资源管理分配单元
- 线程是最小的执行单元
无论是进程还是线程都是需要操作系统来控制的。拿线程来举例,线程有多个执行状态:初始化、可运行、运行中、阻塞、销毁 五种状态。这五种状态的转化关系如下:
线程状态转化图.png
线程的状态转化是由操作系统内核中的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关键字来实现。
代码语言:javascript复制def a():
print 1
yield
print 2
yield
def b():
print 'A'
print 'B'
generator=a()
next(generator)
b()
generator.send('')
该语句就会打印:
代码语言:javascript复制
1
A
B
2
- 通过调用a()生成一个执行器
- 通过next触发执行器执行a并打印1
- 调用b打印A和B
- 通过send像执行器发送信号继续执行并打印2
整个流程是由一个线程执行,通过一些语法来控制执行流程,不涉及到锁,不涉及到线程切换,是通过关键字来辅助a和b协作工作,所以叫做协程。
ES6中引入了for..of..,它本身是一个语法糖,原理是通过引入迭代器(iterator)来实现,而迭代器就是通过协程来实现的。而在ES7中引入了更加直观的async/await`。
java本身不支持协程,但可以通过三方库来实现,例如Quasar和Akka等。