Go Scheduler

简单为说,GMP 分别代表三个核心角色: G(Goroutine)、M(Machine)和 P(Processor)

拆解

G (Goroutine)

Go 协程,是待执行的任务单元,非常轻量,初始栈空间仅约 2KB,它并不直接在操作系统线程上跑,而是由 Go 高度器安排。包含控制信息(如栈指针、状态、程序计数器 PC 等)

M (Machine)

操作系统内核线程(Thread),它是真正的执行者。所有的代码最终都要由 M 跑在 CPU 上,M 数量通常略多于 P(当发生阻塞调用时会创建新 M)。注:M 不直接存储 G 的执行环境,它必须绑定一个 P 才能执行代码

P (Processor)

逻辑处理器,是 G 与 M 之间的“媒人”或“资源池”,P 包含了运行 Go 代码所需的资源(如内存分配状态、本地 G 队列)。数量由 $GOMAXPROCS 决定,通常等于 CPU 核心数。

协作模型

  • 本地队列: 每个 P 维护一个本地的 G 队列(容量通常为 256),当创建一个新的 G 时,优先放入当前 P 的本地队列。
  • 全局队列: 如果本地队列满了,G 就会被放入全局队列。
  • 绑定执行: M 必须绑定一个 P 才能开始工作,M 从 P 的本地队列中弹出 G 来执行。

“聪明”之处

为了保证 CPU 不闲着,调度器设计了两个机制:

Work Stealing(任务窃取机制)

当某个 M 绑定的 P 发现自己的本地队列空了,且全局队列也没有任务时,它会去其他 P 本地队列里一半的 G 过来运行。防止有的 CPU 忙死,有的 CPU 闲死,实现负载均衡

Hand Off(移交机制/抢占式调度)

当 M 执行的某个 G 发起了系统调用(syscall)而阻塞时,M 会释放绑定的 P。此时,P 会寻找(或创建)一个新的 M 来继续处理剩下的 G。不让一个阻塞的调用拖累整个 P 里的其他任务

为什么使用 GMP

GMP 不是让 CPU 变快了,而是通过 “本地队列减少锁竞争”和“用户态轻量级切换”,把原本浪费在内核管理上的 CPU 算力给抢了回来。

posted @ 2026-04-27 08:18  我已有个她  阅读(8)  评论(0)    收藏  举报