Go GMP 调度

Go GMP 调度

Lirous.
3/5/2026
5 min read
从打工人的视角理解 Go 的 GMP 调度模型。没有晦涩的学术语言,只有真实的工程直觉。

最近在看 Go 的并发调度,突然意识到——用打工人的工作逻辑来比喻 GMP,一切都变得贼清楚。

Goroutine 就是打工人

想象一下工地上的工人。每个工人有一个任务清单(函数调用栈),要逐个完成任务。在 Go 里,一个 Goroutine 就是一个轻量级的打工人。

但这些工人不会自己站在工地里等着被分配任务。他们需要一个流程:有人负责把他们分配到具体的工作岗位上,有人管理工作的节奏,还要防止工人过多导致资源崩溃。

这套管理体系,就是 GMP。

GMP 三大角色

G = Goroutine(工人)

工人本身,最轻量级的单位。创建成本很低,Go 程序可以轻松创建几十万个 Goroutine。他们都在等着被分配工作。

// 随随便便创建 10 万个打工人
for i := 0; i < 100000; i++ {
    go doWork(i)
}

M = Machine(工作岗位 / CPU 核心)

M 对应真实的操作系统线程。一个岗位同一时间只能有一个工人在工作。M 的数量大致受限于 CPU 核心数(或者说 GOMAXPROCS),不会无限增长,这样系统才不会被打崩。

P = Processor(工头/经理)

这是我觉得最关键的一个。P 不是处理器本身,而是"调度权限"和"本地工作队列"的拥有者。

想象 P 就是工地的工头:

  • 他有权分配工人到具体的岗位
  • 他手里握着一个本地的工作队列(Local Run Queue)
  • 多个 P 可以并行工作,不互相干扰

调度是怎么玩的

全局工作队列(Global Run Queue)
         ↓
    ┌────────────────────┐
    │   P1 (工头1)        │
    │  本地队列: [G1,G2]  │  ← 关键:有自己的队列
    │  绑定 M1 (岗位1)    │
    └────────────────────┘

    ┌────────────────────┐
    │   P2 (工头2)        │
    │  本地队列: [G3,G4]  │
    │  绑定 M2 (岗位2)    │
    └────────────────────┘

正常流程是这样的:

  1. 分配阶段:P 从自己的本地队列取一个 G(工人),分配给绑定的 M 去执行
  2. 工作中:M 在执行 G 的函数代码
  3. 等等,工人阻塞了:如果 G 被系统调用阻塞(比如磁盘 I/O),这个 M 不能浪费在这儿,必须去干别的。此时 M 会脱离 P,Go 运行时会新建或唤醒另一个 M 来接替 P,继续处理其他 G
  4. 当前 P 的队列空了:P 会去全局队列或者隔壁 P 那儿"偷工作"(work stealing),防止有的岗位忙死、有的岗位闲死

我理解到的关键点

为什么不直接用 OS 线程?

OS 线程太重。上下文切换开销大,创建/销毁成本高。Goroutine 轻到飞起,所以我们可以创建上万个,而不用担心系统资源炸了。

为什么要有 P?

直接让 G 跟 M 一对一,不行吗?答案是:不行。原因有两个:

  • M 数量是有限的(受 CPU 核心数限制),但 G 可以无限创建
  • M 可能被 OS 阻塞,此时需要有人来协调——谁来管理这堆 G?答案就是 P

P 才是真正拥有"调度权"的东西。一个 P 可以管理多个 G,分配给当前绑定的 M 去执行。

最关键的理解:不是 G 和 M 直接配对,而是 P 在中间充当调度器的角色。 这样即使 M 被阻塞,P 也能快速切换到新的 M 继续工作。

踩的小坑

一开始我以为 Goroutine 就是"轻量级线程",以为调度完全是自动无感的。其实不完全是:

// 这种 CPU 密集的代码,就没法自动调度了
// Goroutine 会一直占用 M,其他 G 得不到机会
for {
    i++
}

所以如果写了纯 CPU 密集的代码,最好手动加个 runtime.Gosched() 来让出执行权。

另外,GOMAXPROCS 这个参数决定了 P 的数量(通常等于 CPU 核心数)。在容器环境或者虚拟机里,要特别注意这个值,否则可能导致 Go 程序只用一个核心。

最后

理解 GMP 的最简单方式就是:把 Goroutine 当工人,把 M 当岗位,把 P 当工头,全局队列当任务池。 工头在中间做调度,工人轮流上岗干活,工地不会因为工人多就崩溃。

这就是为什么 Go 在高并发场景下这么能打的原因

这阵子在准备投简历冲实习了,继续怼 Go 底层吧。😤

评论区