GMP 模型说明
Goroutine 调度器的工作是将准备运行的 goroutine 协程分配到正确的工作线程中, 然后投递给对应的某个绑定的核心执行
G
G 代表的是 golang 中的协程 goroutine, 是 Golang 对协程概念的抽象, 居于以下特点
- 相比线程而言, 他是一个轻量级的版本的线程
- 拥有自己的栈空间, 状态, 以及执行的任务函数
- 每一个 G 会分配到一个可用的 P, 并且在 M 上运行
M
M (Machine), 代表是工作线程, 他是操作系统内核线程, 用于和操作系统调度器进行交互, 他的作用是将 P 获取到的 G 协程投递到 CPU 某核进行执行, 他具有以下特点
- M 是 Golang 与操作系统之间的桥梁, 他分支执行 P 分配给他的 G
- M 的数量一般会根据系统资源进行调整
- M 可能会被特定的 G 通过
LockOSThread
锁定, 这种 G 和 M 的绑定确保了特定 Goroutine 可以持续使用同一个线程 - M 会有一个进入自旋状态, 循环查询空闲的 P 列表, 如果获取到了 P, 那么就会解除自旋的状态, 且分配一个线程 M
P
P 是一个 Goroutine 的调度器, 每个 P 拥有一个本地队列, 依次进行投递给 M, P 的数量由 GOMAXPROCS(N) 控制, 默认情况下为当前核数量, 他具有以下特点
- P 是 G 的执行上下文, 他有一个本地队列存储待执行的 G, 负责分配 M 执行那个 G
- P 的数量默认情况是根据环境变量
GOMAXPROCS
决定, 如果数量大于 CPU 的物理线程数量将没有什么意义 - M 必须绑定一个 P 才能执行 Go 代码, 但是 M 可以在没有绑定 P 的情况下执行系统调用或者被阻塞
- P 最大队列长度为 256
P 队列
其中已知 P 会有自己的一个本地队列, 那么系统还有一个 全局的 G 队列, 此队列因为多线程会抢占数据, 所以会有一个锁
P 的本地队列接近无锁的状态, 只有在 Work Stealing 状态下会锁
Work Stealing 机制
在 P 本地队列获取完成后会从全局队列中获取一批 G, 如果全局队列没有了, 那么会从其他 P 队列相对较多的地方偷一半的 G 到本 P 的本地队列中
正文完