阅读本文需要有一定的基础:
- 线程与协程之间的关系
- 线程模型
G & M & P
- Goroutine,协程
- Machine,系统线程,OS调度的最小单位
- Processor,局部调度器,或者叫做「逻辑处理器」,负责调度G,并将之映射到M上
所谓的GMP模型,就是Go自己实现的一套并发调度的模型,自己创建协程,自己调度协程,自己将协程映射到线程上。整体来看,就像线程的多对多模型:
所谓的G就对应用户线程,M就是内核线程,而P就是它们之间的交点,负责管理映射关系、调度协程(有点像LWP的作用)。如果要具象化到GMP模型的示意图的话,借用一下别人的图:
从这张图可以看出来,P之所以叫做逻辑处理器,是对于G而言的,G的视角就是,自己放在P上跑。所以,真正的并行度是由CPU核数、M与P决定的。
整体流程与逻辑
怎么跑起来
讨论GMP之间的逻辑
- M需要绑定P,否则就会去休眠
- 当G在执行阻塞系统调用的时候,M与P会解绑,P会带着自己的G们去找下一个M
- 关于阻塞,主要分为用户态以及系统态的阻塞
- 用户态,比如IO、channel等原因导致的,就会将G放到waiting队列中,M去执行下一个G
- 系统态,则会解绑P
-
G的调度
当创建G的时候,如果本地队列满了,则会放到全局队列中
- M会尝试从全局队列中选取一部分加入自己的本地队列
- M在全局为空的时候,可能会去偷其他MP组合的本地内容
M给我的感觉有点像,被Go程序所占用的线程,Go也会像线程池那样对其进行管理,在没有G的时候,会进入自旋状态
全局调度器schedt
P可以理解为一个M的局部调度器,而schedt则会维护M的全局idle队列、P的全局idle队列、G的全局队列(就绪)以及锁。在进行全局调度,比如P与M解绑回归全局idle队列时,就需要它负责。
Links
文章基本都说的比较详细,不过描述上略有侧重,也许能更好地帮助理解
- https://learnku.com/articles/41728,对goroutine的场景mian
- https://juejin.cn/post/6886321367604527112#heading-13,涉及内部结构,以及查看trace的方法
- https://juejin.cn/post/6844904034449489933,本文选用的图是来自其中