进程, 线程, 协程

进程

进程一般由程序、数据集合和进程控制块三部分组成。

  1. 程序用于描述进程要完成的功能,是控制进程执行的指令集;
  2. 数据集合是程序在执行时所需要的数据和工作区;
  3. 程序控制块(Program Control Block,简称PCB),包含进程的描述信息和控制信息,是进程存在的唯一标志。

进程是程序的实例,一个程序可以有多个实例,但一个实例只能对应一个进程

线程

线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。

一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。一个标准的线程由线程ID、当前指令指针(PC)、寄存器和堆栈组成。而进程由内存空间(代码、数据、进程空间、打开的文件)和一个或多个线程组成

任务调度

大部分操作系统(如Windows、Linux)的任务调度是采用时间片轮转的抢占式调度方式。

多线程与多核

多核(心)处理器是指在一个处理器上集成多个运算核心从而提高计算能力,也就是有多个真正并行计算的处理核心,每一个处理核心对应一个内核线程。

内核线程(Kernel Thread,KLT)就是直接由操作系统内核支持的线程,这种线程由内核来完成线程切换,内核通过操作调度器对线程进行调度,并负责将线程的任务映射到各个处理器上。一般一个处理核心对应一个内核线程,比如单核处理器对应一个内核线程,双核处理器对应两个内核线程,四核处理器对应四个内核线程。

超线程技术就是利用特殊的硬件指令,把一个物理芯片模拟成两个逻辑处理核心

程序一般不会直接去使用内核线程,而是去使用内核线程的一种高级接口——轻量级进程(Lightweight Process,LWP),轻量级进程就是我们通常意义上所讲的线程,也被叫做用户线程。由于每个轻量级进程都由一个内核线程支持,因此只有先支持内核线程,才能有轻量级进程。

线程切换

线程切换的实现一般放在内核,中断处理程序也是放在内核。从这个角度来说,如果线程当前处于用户态,若要发生线程切换,必然是要先要先进入内核态,发生状态切换的。线程切换的原因还可能是其他类型的中断,或者线程自身主动进入等待或者睡眠,这些情况无一例外都是要进入内核的。

线程的切换分两种情况: 同进程内, 和不同进程内切换;

线程模型

一对一模型: 一个用户线程对应一个内核线程

CON

  1. 许多操作系统限制了内核线程的数量,因此一对一模型会使用户线程的数量受到限制;
  2. 许多操作系统内核线程调度时,上下文切换的开销较大,导致用户线程的执行效率下降。

一对多模型: 多个用户线程对应一个内核线程

CON

  1. 如果其中一个用户线程阻塞,那么其它所有线程都将无法执行,因为此时内核线程也随之阻塞了;
  2. 在多处理器系统上,处理器数量的增加对多对一模型的线程性能不会有明显的增加

多对多模型: 多个用户线程对应一个内核线程

PRO

  1. 一个用户线程的阻塞不会导致所有线程的阻塞,因为此时还有别的内核线程被调度来执行;
  2. 多对多模型对用户线程的数量没有限制;
  3. 在多处理器的操作系统中,性能得到提升

协程

协程,英文Coroutines,是一种基于线程之上,但又比线程更加轻量级的存在,这种由程序员自己写程序来管理的轻量级线程叫做『用户空间线程』,具有对内核来说不可见的特性

因为是自主开辟的异步任务,所以很多人也更喜欢叫它们纤程(Fiber),或者绿色线程(GreenThread)

为什么使用协程

  1. 由于现代CPU发展的越来越快,每一个时间片能够处理的指令越来越多,很经常导致一个线程时间片还未用完,而进入了等待数据IO的时间。此时该线程必然要让出CPU,让另一个线程运行
  2. 之后有人提出了协程的概念,让同一个线程在一个CPU时间片内可以处理多个任务,虽然协程的切换也耗费时间,但总体来说要比线程的切换要好很多。因此,使用协程可以比使用线程能够得到更好的处理效果(这个不是一定的,还得跟服务器的任务性质以及协程的调度算法有关系,但一般情况下使用协程会比使用线程有更好的性能)

协程解决什么

协程的目的就是当出现长时间的I/O操作时,通过让出目前的协程调度,执行下一个任务的方式,来消除ContextSwitch上的开销。

避免CPU一直等待IO, 增加延迟 同时可以避免创建过多的线程, 造成更多上下文切换

进程 vs 线程

线程与进程最大的区别在于,线程是调度的基本单位,而进程则是资源拥有的基本单位。

所谓内核中的任务调度,实际上的调度对象是线程;而进程只是给线程提供了虚拟内存、全局变量等资源。

  • 当进程只有一个线程时,可以认为进程就等于线程。
  • 当进程拥有多个线程时,这些线程会共享相同的虚拟内存和全局变量等资源。这些资源在上下文切换时是不需要修改的。
  • 另外,线程也有自己的私有数据,比如栈和寄存器等,这些在上下文切换时也是需要保存的

线程的上下文切换分成两种:

  1. 前后两个线程属于不同进程。此时,因为资源不共享,所以切换过程就跟进程上下文切换是一样
  2. 前后两个线程属于同一个进程。此时,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据

协程 vs 线程

itemsthreadgoroutine
资源占用8M2k
调度所属OS用户
切换开销涉及模式切换、16个寄存器PC/SP/DX
性能创建和销毁开销大
数据同步依赖锁只有一个线程,不存在同时写变量冲突,在协程中控制共享资源不加锁

寄存器

  • PC 程序寄存器 - 程序指定地址
  • SP 栈地址
  • DX 数据寄存器

Reference

博客园-博客