GOMAXPROCS在Go语言中如何影响并发性能?
摘要:GOMAXPROCS 决定: 同一时间最多有多少个 goroutine 可以并行运行(占用 CPU)。 更准确说: 它决定 P(Processor)的数量。 一、回忆一下 GMP 模型 我们之前讲过: G = goroutine(任务) M
GOMAXPROCS 决定:
同一时间最多有多少个 goroutine 可以并行运行(占用 CPU)。
更准确说:
它决定 P(Processor)的数量。
一、回忆一下 GMP 模型
我们之前讲过:
G = goroutine(任务)
M = 线程(干活的)
P = 调度资源(必须持有才能执行 G)
关键规则:
M 必须拿到 P 才能执行 G
而:
P 的数量 = GOMAXPROCS
二、默认值是多少?
从 Go 1.5 开始:
默认情况下:
GOMAXPROCS = 当前机器可用的 CPU 核心数
更准确地说:
Go 会设置为“当前进程可用的逻辑 CPU 数量”,也就是默认会把所有可用 CPU 都用上。
什么是“逻辑 CPU”?
不是物理核心数,而是:
包含超线程(Hyper-Threading)
包含容器 / cgroup 限制后的 CPU 数
例如:
4 核 8 线程的机器
→ 默认可能是 8
Docker 限制 2 核
→ 默认是 2
例如:
4 核 CPU → 默认 GOMAXPROCS = 4
8 核 CPU → 默认 GOMAXPROCS = 8
你可以打印看看:
fmt.Println(runtime.GOMAXPROCS(0))
传 0 表示“只读取,不修改”。
从 Go 1.5 开始:
默认值改为 runtime.NumCPU()
不再默认 1
在 Go 1.5 之前:
默认是 1
那时多核不会自动利用。
为什么默认 P 数量等于 CPU 核心数?
这是性能策略,不是物理绑定。
因为:
如果有 4 核 CPU
同时最多只能 4 个线程真正并行
所以:
P 数量 = 并行度上限
通常设为 CPU 数最合理。
但这只是默认策略。
为什么说 P ≠ CPU?
① P 是可以随时改变数量的
runtime.GOMAXPROCS(1)
你可以让:
8 核机器
只使用 1 个 P
CPU 还是 8 核。
说明:
P 是软件控制的。
② P 不绑定具体 CPU 核心
P 不会说:
“我就是 CPU 核心 3。”
线程 M 由操作系统调度到哪个核心,Go 无法控制。
所以:
M 在哪个核心运行是 OS 决定的。
③ M 才是跑在 CPU 上的
真正运行代码的是:
操作系统线程(M)
而不是 P。
P 只是:
决定这个线程有没有资格运行 goroutine。
CPU 决定“最多能跑多少线程”
P 决定“Go 允许多少 goroutine 同时跑”
P 的真正意义是:
控制并行度 + 减少锁竞争 + 支持本地运行队列 + 协助 GC
它是一个“调度隔离单元”。
不是硬件映射。
三、它真正控制的是什么?
很多人误解为:
控制 goroutine 数量 ❌
不是。
它控制的是:
最多同时运行的 goroutine 数量
举个例子:
你开 1000 个 goroutine。
如果:
GOMAXPROCS = 1
那:
任何时刻只有 1 个 goroutine 在真正跑
其他 999 个在排队
如果:
GOMAXPROCS = 4
那:
最多 4 个 goroutine 同时跑在 4 个 CPU 上
四、一个直观类比
想象:
goroutine 是工人
CPU 是机器
P 是机器许可证
你可以雇 1000 个工人(goroutine)
但如果:
只有 4 台机器(P=4)
同时只能 4 个工人干活。
