乐趣区

关于系统:进程线程以及协程的区别

一、并发和并行

1.1 并发

在操作系统中,一个时间段内有几个程序都处于正在运行的状态,而且这几个程序都是在同一个处理机上运行,但 任意一个时刻其实只有一个程序在处理机上运行

在一个只有单核(单 CPU)的处理器的操作系统中,同一时刻只能有一个过程运行。

假如只有一个过程运行,为了执行多任务,须要将 CPU 的工夫资源分为很多个工夫片,将每个工夫片分给一个线程,每个线程就能够执行不同的工作。

这样做的益处是,每个线程只占用一个工夫片,当一个工作阻塞,当耗尽了内核调配给它的一个工夫片后就会挂起,接着执行其余工作,后续再切换到阻塞的线程时也只能占用一个工夫片的工夫。

有些文章说,内核将工夫片分给过程,其实不算精确,因为线程才是程序理论运行时的单元,过程只是一个容器,起码蕴含一个线程。当多个程序运行时,内核外表上是会将工夫片调配给过程,但实际上是依据过程里的线程数调配工夫的。

1.2 并行

当初的市面上曾经没有单核处理器了,最低端的处理器也是多核(多 CPU)。与单核同样,每个外围同一时刻也只能运行一个过程。

同样假如每个外围只有一个过程,如果每个过程上都只运行一个程序(只开一个线程),这些程序因为是运行在不同的外围上,占用的不是同一个 CPU 资源,所以能够在 同一时刻运行,且互不烦扰,这就是并行。

1.3 二者的区别

并发和并行的区别就在于 同时 二字。

尽管并发和并行都能运行多个程序,但区别就在于:

  • 并发是多个程序交替运行,因为工夫片很短,用户并不会感觉到

    • 工夫片的调配规范也是以可感知水平设计的,Linux 的工夫片范畴为 5ms ~ 800ms
  • 并行是多个程序同时运行

二、过程、线程和协程在内存上的区别

2.1 过程内存

过程是零碎进行资源分配的最小单位,是操作系统构造的根底。

在过程中,运行的程序中会产生一个独立的内存体,这个内存体内有本人独立的内存空间,有本人的堆,下级挂靠的是操作系统。

操作系统会以过程为单位调配系统资源(CPU 工夫片、内存等资源)。

过程的内存占用在 32 位零碎中为 4G,64 位零碎能够达到 T 级。

2.2 线程内存

线程是零碎可能运行运算调度的最小单位。

一条线程是过程中的一个繁多程序的控制流,一个过程中能够并发多个线程,每个线程在宏观上并行(宏观串行)执行不同的工作。

同一过程中的多条线程共享该过程中的全副系统资源,如虚拟地址空间、文件描述符等。但每一个线程都有各自的调用栈、独立的寄存器环境、线程本地存储。

2.3 协程内存

协程,又被称为微线程,顾名思义,就是轻量级的线程。

在协程初始化创立的时候为其调配的栈为 2kB(不同语言的协程的栈内存可能不同,同一语言的不同版本也可能不同,此处以 Go 1.4+ 的协程为例),而线程栈要比这个数字大得多,Linux 零碎上能够通过 ulimit -s 命令来查看线程内存占用。

$ ulimit -s
8192 Kb

在高并发 web 服务器中,如果为每个申请创立一个协程去解决,100 万并发只须要 2G 内存,如果应用线程,须要的内存高达数 T。

2.3.1 粗略计算内存占用

上面应用应用 Go 代码简略计算一下协程的内存占用:

package main

import ("time")

func main() {
    for i := 0; i < 1000000; i++ {go func() {time.Sleep(5 * time.Second)
        }()}
    time.Sleep(10 * time.Second)
}

系统资源:

$ grep MemTotal /proc/meminfo
MemTotal:        4017728 kB
$ getconf LONG_BIT
64

程序运行前内存状况:

top - 12:22:31 up  1:49,  2 users,  load average: 0.02, 0.01, 0.00
Tasks: 117 total,   1 running, 116 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  4017728 total,  3259556 free,   140280 used,   617892 buff/cache
KiB Swap:   998396 total,   998396 free,        0 used.  3637188 avail Mem

程序运行完休眠时内存状况:

top - 12:23:25 up  1:50,  2 users,  load average: 0.09, 0.03, 0.01
Tasks: 119 total,   1 running, 118 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  4017728 total,   631380 free,  2767216 used,   619132 buff/cache
KiB Swap:   998396 total,   998396 free,        0 used.  1010280 avail Mem

前后 free 的内存别离为 $3259556$ 和 $631380$,所以每个协程占用的内存为 $(3259556-631380)\div1000000=2.628176$,约为 2.6 kB。

三、切换开销

过程、线程和协程在进行切换时都会有肯定的性能耗费,这种耗费通常被叫作切换开销。

3.1 过程切换

过程切换分为两步:

  1. 切换页目录以应用新的地址空间
  2. 切换内核栈和硬件上下文

所以在切换过程时,肯定会有两个问题:

  1. 新的过程须要新的内存空间,将寄存器中的内容切换进去是最显著的性能耗费
  2. 上下文的切换会扰乱处理器的缓存机制。一旦切换上下文,处理器中所有曾经缓存的内存在址在一瞬间全副作废,已缓存的页表被全副刷新,导致内存拜访在一段时间内相当低效,程序运行也会变慢甚至呈现卡顿。

3.2 线程切换

线程应用的是过程的内存资源,所以在切换线程时不须要切换虚拟内存空间。

但在切换上下文时,一样会消耗 CPU 工夫,和过程切换的开销相差不大(几微秒)。

3.3 协程切换

协程的切换齐全不同:

  1. 协程切换过程 齐全在用户空间产生。把以后协程 $A$ 的 CPU 寄存器状态保存起来,而后将须要切换进来的协程 $B$ 的 CPU 寄存器状态加载到 CPU 寄存器上就能够。
  2. 协程切换的过程要比过程和线程切换做的事更少

一次协程的上下文切换最多须要几十纳秒的工夫。

退出移动版