进程、线程、协程实际上都是为并发而生,但是他们的各自的模样是完全不一致的,下面我们来分析一下他们各自的特点和关系。

进程内存

进程,可执行程序运行中形成一个独立的内存体,这个内存体有自己独立的地址空间(Linux会给每个进程分配一个虚拟内存空间),有自己的堆,上级挂靠单位是操作系统。操作系统会以进程为单位,分配系统资源(CPU时间片、内存等资源),进程是资源分配的最小单位

进程模型

线程内存

线程,有时被称为轻量级进程(Lightweight Process,LWP),是操作系统调度(CPU调度)执行的最小单位

线程模型

多个线程共同“寄生”在一个进程上,除了拥有各自的栈空间,其他的内存空间都是一起共享。所以由于这个特性,使得线程之间的内存关联性很大,互相通信就很简单(堆区、全局区等数据都共享,需要加锁机制即可完成同步通信),但是同时也让线程之间生命体联系较大。

执行单元

对于Linux来讲,不区分进程还是线程,他们都是一个单独的执行单位,CPU一视同仁,均分配时间片。

cpu分配时间片示意图

切换问题与协程

我们通过上述的描述,可以知道,线程越多,进程利用(或者)抢占的cpu资源就越高。我们知道,当**cpu在内核态切换一个执行单元**的时候,会有一个时间成本和性能开销

  • 切换内核栈

  • 切换硬件上下文

  • CPU高速缓存失效

  • 保存寄存器中的内容

    将之前执行流程的状态保存。

等等

综上,我们不能够大量的开辟,因为线程执行流程越多,cpu在切换的时间成本越大。很多编程语言就想了办法,既然我们不能左右和优化cpu切换线程的开销,那么,我们能否让cpu内核态不切换执行单元, 而是在用户态切换执行流程呢?

很显然,我们是没权限修改操作系统内核机制的,那么只能在用户态再来一个伪执行单元,那么就是协程了。

CPU与协程示意图

协程的切换成本

协程切换比线程切换快主要有两点:

(1)协程切换完全在用户空间进行;线程切换涉及特权模式切换,需要在内核空间完成

(2)协程切换相比线程切换做的事情更少,线程需要有内核和用户态的切换,系统调用过程。

协程切换成本

协程切换非常简单,就是把当前协程的 CPU 寄存器状态保存起来,然后将需要切换进来的协程的 CPU 寄存器状态加载的 CPU 寄存器上就 ok 了。而且完全在用户态进行,一般来说一次协程上下文切换最多就是几十ns 这个量级。

线程切换成本

系统内核调度的对象是线程,因为线程是调度的基本单元(进程是资源拥有的基本单元,进程的切换需要做的事情更多,这里占时不讨论进程切换),而线程的调度只有拥有最高权限的内核空间才可以完成,所以线程的切换涉及到用户空间和内核空间的切换,也就是特权模式切换,然后需要操作系统调度模块完成线程调度,而且除了和协程相同基本的 CPU 上下文,还有线程私有的栈和寄存器等,说白了就是上下文比协程多一些。