Last updated
Last updated
谈到 goroutine
,绕不开的一个话题是:它和 thread
有什么区别?
参考资料 告诉我们可以从三个角度区别:内存消耗、创建与销毀、上下文切换。
内存占用
创建一个 goroutine
的栈内存消耗为 2 KB
,而 thread
占用 1M
以上空间
对于一个用 Go
构建的 HTTP Server 而言,我们可以给每个请求创建一个 goroutine
。而如果用一个使用线程作为并发原语的语言构建的服务,例如 Java 来说,每个请求对应一个线程则太浪费资源了,很容易内存溢出。
创建和销毀的开销
Go
语言里对应线程的结构是 M
,创建 M
需要调用系统函数 clone()
,创建和切换都需要进入内核,所消耗的性能代价比较高,开销较大
goroutine
是用户级的,不需要切换到内核态,创建和销毁的开销非常小。
上下文切换
当 thread
切换时,需要保存各种寄存器,以便将来恢复。并且也需要陷入内核态
而 goroutines
切换只需保存三个寄存器:PC (Program Counter 程序计数器), SP (Stack Pointer 栈顶指针) and BP (Base Pointer 基数指针计数器)
。
一般而言,线程切换会消耗 1000-1500 ns
,goroutine
的切换约为 200 ns
Go
调度器还有助于提高 cache-line
的效率和 NUMA
。从 OS
的视角,OS
线程没有换,也就从来没有进入到等待状态,一直在同一个 CPU
核上。
两级线程模型 (M: N)
goroutine建立在操作系统线程基础之上,它与操作系统线程之间实现了一个多对多 (M:N) 的两级线程模型。
这里的 M:N 是指M个goroutine运行在N个操作系统线程之上,内核负责对这N个操作系统线程进行调度,而这N个系统线程又负责对这M个goroutine进行调度和运行。