2. GMP 模型总览:Goroutine、Machine、Processor 三大核心组件及其关系
Go 语言的并发调度模型被称为 GMP 模型,它是 Go 运行时(runtime)实现高并发、轻量级调度的核心架构。理解 GMP 是掌握 Go 并发性能调优的基石。本章将逐一拆解三大核心组件——Goroutine (G)、Machine (M)、Processor (P),并阐明它们之间的协作关系。
2.1 三大核心组件定义
| 组件 | 全称 | 核心职责 | 数量级 / 约束 |
|---|---|---|---|
| G | Goroutine | 轻量级用户态线程,承载用户任务(函数/闭包) | 百万级;由 Go 运行时创建和销毁 |
| M | Machine (内核线程) | 实际执行 Go 代码的底层操作系统线程 | 通常数千;与内核线程 1:1 对应 |
| P | Processor (逻辑处理器) | 调度上下文,管理可运行的 G 队列,为 M 提供执行资源 | 默认等于 CPU 核心数;由 GOMAXPROCS 控制 |
关键区别: G 是任务载体,M 是执行者,P 是调度器。没有 P,M 无法执行任何 G。
2.2 组件内部结构速览
Goroutine (G)
- 栈:初始 2KB,可动态增长(上限 1GB),远小于内核线程栈。
- 状态:
_Gidle、_Grunnable、_Grunning、_Gsyscall、_Gwaiting等。 - 调度信息:保存 PC、SP、BP 等寄存器上下文,用于恢复执行。
- 关联 M:当前正在执行该 G 的 M(仅在
_Grunning时有效)。
Machine (M)
- 线程结构:对应一个操作系统线程,包含 TLS(线程本地存储)。
- 当前 G:
curg指针,指向正在执行的 G。 - 自旋状态:
spinning标志,表示 M 正在空闲寻找可运行 G。 - 关联 P:
p指针,绑定一个 P 才能执行 G。
Processor (P)
- 本地运行队列 (LRQ):每个 P 拥有一个长度为 256 的环形队列,存放
_Grunnable状态的 G。 - 全局运行队列 (GRQ):全局共享队列,当 LRQ 满时 G 放入 GRQ。
- 空闲 G 列表:缓存已退出的 G,用于复用,减少内存分配。
- 关联 M:当前绑定的 M,若为
nil表示 P 空闲。
2.3 GMP 核心关系:调度循环
GMP 模型的核心调度逻辑可以概括为:M 绑定 P,P 从队列中获取 G,M 执行 G。当 G 发生阻塞(如系统调用、Channel 操作)或主动让出时,调度器会切换 G,实现高并发。
调度循环口诀: M 找 P,P 找 G,G 跑任务,阻塞换人。
2.4 GMP 关系架构图
下图展示了 GMP 三大组件之间的静态绑定与动态调度关系:
2.5 核心调度流程:M 如何获取 G 并执行
- M 绑定 P:M 必须与一个 P 绑定才能执行 Go 代码。空闲 M 会尝试从全局空闲 P 列表获取一个 P。
- 从 LRQ 获取 G:M 优先从绑定的 P 的本地运行队列(LRQ)头部获取一个
_Grunnable状态的 G。 - 从 GRQ 获取 G:若 LRQ 为空,M 会从全局运行队列(GRQ)获取一批 G(通常取 1 个,并尝试窃取其他 P 的 G)。
- 执行 G:M 设置
curg为获取到的 G,恢复其上下文并开始执行。 - G 阻塞或退出:
- 若 G 主动让出(如
runtime.Gosched()),G 放回 LRQ 尾部,M 寻找下一个 G。 - 若 G 发生系统调用阻塞,M 与 P 解绑,P 寻找新的 M 继续执行其他 G;阻塞的 M 等待系统调用返回后重新绑定 P。
- 若 G 执行完毕,G 被回收至 P 的空闲 G 列表,M 继续取下一个 G。
- 若 G 主动让出(如
性能陷阱: 如果 G 频繁发生系统调用(如大量文件 I/O、网络 I/O 未使用 epoll),会导致 M 频繁与 P 解绑和重绑定,增加调度开销。此时应使用异步 I/O 或调整
GOMAXPROCS 来缓解。
2.6 工作窃取(Work Stealing)机制
当某个 P 的 LRQ 为空时,该 P 绑定的 M 不会空闲等待,而是尝试从其他 P 的 LRQ 尾部窃取一半的 G(最多 128 个)。这种机制保证了负载均衡,避免某些 P 忙死而其他 P 空闲。
| 场景 | 窃取目标 | 窃取数量 |
|---|---|---|
| 本地 LRQ 为空 | 随机选择一个其他 P | 目标 LRQ 长度的一半(上限 128) |
| 所有 P 的 LRQ 都为空 | 全局运行队列 (GRQ) | 1 个(若 GRQ 非空) |
| GRQ 也为空 | M 进入自旋状态 | 等待新 G 被创建或唤醒 |