Featured image of post Go-GMP模型学习

Go-GMP模型学习

深入剖析 Go 语言高性能并发的核心——GMP 调度模型的工作原理与演进历程。

Go 语言 GMP 模型学习

一句话总结

Go 的调度器任务就是:把数以万计的协程(G),通过调度中介(P),均匀地打散到有限的 CPU 核心(M)上运行。

极致压缩:P 调度 M 执行 G

核心角色 (GMP 是什么?)

角色全称比喻职责
GGoroutine订单 (外卖)你写的 go func()。它只是存放代码和执行状态的轻量级对象。
MMachine外卖员 (骑手)真正的物理执行单元(内核线程)。没有它,代码跑不起来。
PProcessor区域经理 (调度站)负责分配资源。它持有 G 的本地队列,并绑定一个 M 来干活。

架构拓扑 (ASCII 图解)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
   +--------------------------+
   |  Global Queue (全局队列)  | <--- 存放暂时没地方去的 G
   +--------------------------+
             |
             v
      +--------------+        +--------------+
      |      P1      |        |      P2      |  <--- 逻辑处理器 (数量 = CPU核数)
      +--------------+        +--------------+
      | Local Queue  |        | Local Queue  |  <--- 存放待运行的 G
      +--------------+        +--------------+
             |                       |
             v                       v
      +--------------+        +--------------+
      |      M1      |        |      M2      |  <--- 绑定内核线程执行
      +--------------+        +--------------+

调度策略:为什么它这么快?

Go 调度器之所以高效,全靠以下这两个“偷懒”和“互助”策略:

  1. Work Stealing (任务窃取机制)

如果 P2 的本地队列空了,那么 P2 会从 P1 的队列里面偷一半过来运行。 效果:这个机制绝对不会让CPU闲着

  1. Hand Off (接力机制)

G1M1 上发起了一个阻塞的系统调用(比如读文件):

  • P1 会立刻放弃 M1,带着剩下的 G 去找一个新的 M3 绑定
  • M1 带着 G1 慢慢等 IO 完成

效果:一个M阻塞不会拖垮整个队列

调度生命周期 (极简版)

  1. 创建:go func() 创建一个 G ,给 P 管理,放入道 P 的本地队列
  2. 执行:P 从本地队列里拿一个 GM 并运行
  3. 抢占:如果一个 G 运行超过 10ms ,Go 虚拟机会发送信号把它踢下去,换下一个M
  4. 退出:G执行完毕,被放入闲置列表,等待下次复用

参考链接与素材

使用 Hugo 构建
主题 StackJimmy 设计