5-1.调度-线程
goroutine 和 thread 的区别
谈到 goroutine
,绕不开的一个话题是:它和 thread
有什么区别?
参考资料 How Goroutines Work 告诉我们可以从三个角度区别:内存消耗、创建与销毀、上下文切换。
内存占用
创建一个
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进行调度和运行。
Last updated