进程(process)和线程(thread)应该是技术面试中最经常问到的知识点之一了。本文尝试简单总结下,进程与线程的一些区别。
在一开始,计算机并没有「进程」、「线程」这样的概念,计算机的功能就是输入 → 计算 → 输出。绝大部分时间里,其实计算机都是在等待用户的输入,显然这样的方式非常低效,毕竟人的手速怎么也无法跟计算机运算速度相提并论。
于是,人们开始把要执行的指令预算写到纸带、磁带或磁盘等介质上,然后将这些指令交给计算机去执行。一个专门的程序负责从对应的存储介质上读取指令序列,并交给计算机进行执行。计算机的执行过程不需要再人肉实时输入指令,少了大量等待时间,效率自然上升了很多很多。这个专门的程序,就是原始的操作系统——批处理操作系统。
批处理操作系统虽然解决了程序执行的性能问题,但是还是有一个缺点,计算机同时只能执行一个任务(程序)。如果这个程序需要大量 IO 操作,在 IO 操作发生的过程中,其实 CPU 是空闲的,要是这个空闲时间可以被利用起来,那就更好了。
为了进一步提升计算机的运算效率,「进程」就此诞生了。用进程的概念来抽象上面提到的一个任务(程序),每个程序就是一个进程,有自己独立的内存空间,程序间互不相关,操作系统负责来调度各个程序。这时 CPU 虽然还是单核的,但是操作系统通过分时的方式,把 CPU 的时间分成非常多的片段,每个片段内执行一个进程的指令,当这个时间片结束之后操作系统负责调度 CPU 执行另一个程序的指令。从 CPU 的角度来看,同一时间还是只有一个程序在执行,但是由于这里的时间片非常非常小,人的视角上,感受到的就像是多个程序在一起「同时」运行一样。
进程虽然有各自独立的内存空间,多个进程间互不相关。但是很多场景下其实需要两个程序有办法传递信息(通信),如果两个进程运行的时候如果有办法通过某种方式进行通信,会让程序的设计更加灵活方便。于是,进程间通信出现了,像管道、消息队列、信号等等,都可以实现两个进程间通信。
多进程的出现使得一个 CPU 可以「同时」执行多个不同的程序,但是在一个程序内部,指令依然是串行处理的。其实很多场景下,就算是一个程序内部,多个子任务也可以被设计为独立并行执行。为了让一个程序内部的多个子模块可以并行执行,「线程」的概念诞生了。线程是对进程内子任务的抽象,一个进程可以有多个子任务(线程)在同时执行,这些子任务会共享同一个进程的数据。「线程」的概念出现后,操作系统调度的最小单位变成了线程,而进程成为了操作系统分配资源的最小单位。
多线程多进程的出现虽然让多任务并行处理的性能大大提升,但其实 CPU 角度上看本质所有的任务还是在串行执行,并不是真正的并行。直到多核 CPU 的出现,操作系统负责将不同的线程调度到多个 CPU 上执行,实现真正意义上的并行执行。
在编程开发中,其实并不是使用多线程、多进程就一定是好的,需要考虑具体的场景,根据不同业务场景选择合适的模型。比如 Redis 是单进程单线程,而 Nginx 可以使用多进程也可以使用多线程,他们却都是性能非常高的应用。