GMP 是 Go 调度器的三层结构:G(goroutine)是任务实体,保存栈、指令指针和状态;M(machine)封装 OS 线程,真正执行代码;P(processor)是调度上下文,数量由 GOMAXPROCS 决定,每个 P 持有本地可运行队列。M 必须绑定 P 才能执行 G,P 的数量决定最大并行度。

调度要点:新 G 优先进入当前 P 的本地队列(每 61 次调度穿插检查一次全局队列防饥饿),本地满了把一半迁入全局队列;P 空闲时先查全局队列和网络轮询器,再从其他 P 偷一半 G(work stealing);G 发生系统调用阻塞时 M 与 P 解绑(hand off),P 挂到其他 M 上继续跑,保证 syscall 不拖垮整体吞吐;channel、锁阻塞时 G 进等待队列,P 直接调度下一个 G。

抢占:Go 1.14 起基于信号的异步抢占,运行超过约 10ms 的 G 会被 sysmon 强制让出,解决纯计算循环饿死调度器的问题。

// 容器内 CPU quota 与 GOMAXPROCS 错配是常见性能问题
// 可用 uber/automaxprocs 自动适配 cgroup 限制

追问方向:为什么引入 P(解耦并行度与线程数、缓存局部性)、网络轮询器与 sysmon 的分工。