本文会简单介绍并行编程里经常会遇到的一些基本概念, 及这些不同概念之间的区别; 包括并发和并行; 进程, 线程及协程.

并发 (Concurrent) 和并行 (Parallel)

并发和并行的主要区别是实际执行代码的物理部件 (也就是 CPU) 的数量是不一样的.

并发是指在一个 CPU 上分时执行多个任务. 这样做的好处是:

  • 能同时执行多个任务; 以前电脑只有一个 CPU 的时候, 就是通过并发来实现同时打开多个程序; 当 CPU 的运算速度足够快时, 用户就感觉好像是这些程序在同时运行;
  • 当某些任务因为 IO 或网络访问被阻塞时, 可以执行别的计算密集型任务; 这样可以提高程序的执行效率, 提高 CPU 的利用率;

和并发相对应, 并行就是指多个任务同时跑在不同的 CPU 上; 并行是真正的多任务执行, 在正确地设计及编程下, 能成倍地提高程序的执行效率.

进程和线程

进程和线程都是由操作系统来负责调度的, 操作系统正是通过他们来对任务进行抽象, 并调度到 CPU 上执行的.

线程的出现是因为进程存在以下缺点:

  • 不同进程的切换开销很高;
  • 不同进程间的数据共享很困难;

相比于进程:

  • 线程切换开销更小; 只需要更改 CPU 寄存器及线程堆栈这些上下文信息, 不需要更改页表等信息;
  • 同一进程下的不同线程的内存是共享的, 方便了数据的共享; 因为没有切换内存页表信息, 所有的线程看到的内存都是一样的;

不同于进程之间的资源是隔离的, 一个进程的崩溃不会影响另外一个进程; 线程共享了很多资源, 同一个进程下的某个线程崩溃, 可能会导致这个进程内的其它线程的崩溃.

用户级线程和内核级线程

基于是在用户程序中还是在内核中实现对线程的支持, 可以把线程分为用户级线程和内核级线程, 这两种线程的主要区别是:

  • 内核支持线程是OS内核可感知的,而用户级线程是OS内核不可感知的。
  • 用户级线程的创建、撤消和调度不需要OS内核的支持,是在语言(如Java)这一级处理的;而内核支持线程的创建、撤消和调度都需OS内核提供支持,而且与进程的创建、撤消和调度大体是相同的。
  • 用户级线程执行系统调用指令时将导致其所属进程被中断,而内核支持线程执行系统调用指令时,只导致该线程被中断。
  • 在只有用户级线程的系统内,CPU调度还是以进程为单位,处于运行状态的进程中的多个线程,由用户程序控制线程的轮换运行;在有内核支持线程的系统内,CPU调度则以线程为单位,由OS的线程调度程序负责线程的调度。
  • 用户级线程的程序实体是运行在用户态下的程序,而内核支持线程的程序实体则是可以运行在任何状态下的程序。

这篇文章中会有更详细的介绍.

Linux 中的轻量级进程 (Light Weight)

linux 内核不存在整真正意义上的线程. linux将所有的执行实体都称之为任务 (task), 每一个任务都类似于一个单线程的进程, 具有内存空间、执行实体、文件资源等. 但 Linux 下不同任务之间可以选择公用内存空间, 因而在实际意义上, 共享同一个内存空间的多个任务构成了一个进程, 而这些任务就成为这个任务里面的轻量级进程.

协程

协程其实就是轻量级的用户级线程, 一般直接由编程语言的 runtime 来实现协程的创建、调度等, 内核无法感知协程的存在.

goroutine 通过 G-P-M 模型提高了协程的并行性, 防止单个协程阻塞时影响整个进程的运行.

References