Go调度
系统级别的调度依然是 进程和线程.虽然线程比较轻量,但是在调度时也有比较大的额外开销。每个线程会都占用 1M 以上的内存空间,在切换线程时不止会消耗较多的内存,恢复寄存器中的内容还需要向操作系统申请或者销毁资源,每一次线程上下文的切换都需要消耗 ~1us 左右的时间1,但是 Go 调度器对 Goroutine 的上下文切换约为 ~0.2us,减少了 80% 的额外开销2。
图-进程和线程
Go是如何做到的呢?
- Go 语言的调度器通过使用与 CPU 数量相等的线程减少线程频繁切换的内存开销
- 在每一个线程上执行额外开销更低的 Goroutine 来降低操作系统和硬件的负载。
图-Goroutine和线程
图-Go语言调度器G-M-P模型
- G — 表示 Goroutine,它是一个待执行的任务;
- M — 表示操作系统的线程,它由操作系统的调度器调度和管理;
- P — 表示处理器,它可以被看做运行在线程上的本地调度器;
P作为调度器,核心的调度策略由两个队列维护.一个是全局的队列,一个是本地的队列.全局队列用来处理本地队列为空或者满的P. 为空时会从全局队列中”偷”取,满时会将新加入的goroutie放入全局队列
goroutie在执行过程中,碰到一些阻塞式的调用时,另一个goroutie会被执行. 而goroutie的处理则根据定义的各种状态来判断处理.
- blocking syscall (for example opening a file)
- network input
- channel operations
- primitives in the sync package.
待续.(涉及到一些协程和线程的状态流转和策略比较复杂.后续明白了再更吧 :confused:)
Reference
golang的系列在draveness的博客中有一些列非常详细的介绍.本篇都只是描述一些个人认为的重点内容.如果想知道相关golang设计过程,源码,机器执行指令等内容.推荐直接看draveness的内容或者其他更为详细的博文.
https://draveness.me/golang/