5-1.调度-线程

goroutine 和 thread 的区别

谈到 goroutine,绕不开的一个话题是:它和 thread 有什么区别?

参考资料 How Goroutines Work 告诉我们可以从三个角度区别:内存消耗、创建与销毀、上下文切换。

  1. 内存占用

    • 创建一个 goroutine 的栈内存消耗为 2 KB,而 thread 占用 1M 以上空间

    • 对于一个用 Go 构建的 HTTP Server 而言,我们可以给每个请求创建一个 goroutine 。而如果用一个使用线程作为并发原语的语言构建的服务,例如 Java 来说,每个请求对应一个线程则太浪费资源了,很容易内存溢出。

  2. 创建和销毀的开销

    • Go 语言里对应线程的结构是 M,创建 M 需要调用系统函数 clone(),创建和切换都需要进入内核,所消耗的性能代价比较高,开销较大

    • goroutine 是用户级的,不需要切换到内核态,创建和销毁的开销非常小。

  3. 上下文切换

    • thread 切换时,需要保存各种寄存器,以便将来恢复。并且也需要陷入内核态

    • goroutines 切换只需保存三个寄存器:PC (Program Counter 程序计数器), SP (Stack Pointer 栈顶指针) and BP (Base Pointer 基数指针计数器)

    • 一般而言,线程切换会消耗 1000-1500 nsgoroutine 的切换约为 200 ns

    • Go 调度器还有助于提高 cache-line 的效率和 NUMA。从 OS 的视角,OS 线程没有换,也就从来没有进入到等待状态,一直在同一个 CPU 核上。

用户级线程模型

两级线程模型 (M: N)

goroutine建立在操作系统线程基础之上,它与操作系统线程之间实现了一个多对多 (M:N) 的两级线程模型。

这里的 M:N 是指M个goroutine运行在N个操作系统线程之上,内核负责对这N个操作系统线程进行调度,而这N个系统线程又负责对这M个goroutine进行调度和运行。

Last updated