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 个工人干活。
阅读全文