Go 语言 GMP 模型学习
一句话总结
Go 的调度器任务就是:把数以万计的协程(G),通过调度中介(P),均匀地打散到有限的 CPU 核心(M)上运行。
极致压缩:P 调度 M 执行 G
核心角色 (GMP 是什么?)
| 角色 | 全称 | 比喻 | 职责 |
|---|---|---|---|
| G | Goroutine | 订单 (外卖) | 你写的 go func()。它只是存放代码和执行状态的轻量级对象。 |
| M | Machine | 外卖员 (骑手) | 真正的物理执行单元(内核线程)。没有它,代码跑不起来。 |
| P | Processor | 区域经理 (调度站) | 负责分配资源。它持有 G 的本地队列,并绑定一个 M 来干活。 |
架构拓扑 (ASCII 图解)
| |
调度策略:为什么它这么快?
Go 调度器之所以高效,全靠以下这两个“偷懒”和“互助”策略:
- Work Stealing (任务窃取机制)
如果 P2 的本地队列空了,那么 P2 会从 P1 的队列里面偷一半过来运行。 效果:这个机制绝对不会让CPU闲着
- Hand Off (接力机制)
当 G1 在 M1 上发起了一个阻塞的系统调用(比如读文件):
- P1 会立刻放弃 M1,带着剩下的 G 去找一个新的 M3 绑定
- M1 带着 G1 慢慢等 IO 完成
效果:一个M阻塞不会拖垮整个队列
调度生命周期 (极简版)
- 创建:
go func()创建一个 G ,给 P 管理,放入道 P 的本地队列 - 执行:P 从本地队列里拿一个 G 给 M 并运行
- 抢占:如果一个 G 运行超过 10ms ,Go 虚拟机会发送信号把它踢下去,换下一个M
- 退出:G执行完毕,被放入闲置列表,等待下次复用
