当前位置: 首页 > news >正文

如何理解 Go 的调度模型,以及 G / M / P 各自的职责

一、为什么需要调度模型?

先问一个问题:

Go 里可以同时开成千上万个 goroutine,它是怎么做到的?

如果每个 goroutine 都对应一个操作系统线程,那系统早就炸了。

因为:

  • 线程很重(几 MB 栈空间)
  • 创建销毁成本高
  • 线程切换开销大

所以 Go 自己实现了一套“用户级调度器”。


二、Go 的整体调度模型(GMP 模型)

Go 使用的是:

G - M - P 调度模型

可以理解为:

G(任务)
M(干活的工人)
P(调度中心 / 工作许可证)

一个简单类比:

现实世界 Go 中
任务 G (goroutine)
工人 M (machine 线程)
工头 / 任务队列管理 P (processor)

三、G / M / P 分别是什么?


1️⃣ G —— Goroutine(真正的任务)

G 就是:

你写的每一个 goroutine

例如:

go func() {fmt.Println("hello")
}()

这就是创建了一个 G。

G 里面包含什么?

  • 要执行的函数
  • 栈空间(初始 2KB,可自动扩容)
  • 程序计数器
  • 状态(运行中、等待中等)

G 是“轻量级线程”。


2️⃣ M —— Machine(真正干活的线程)

M 是:

操作系统线程

它是真正执行代码的“工人”。

⚠️ 注意:

  • M 是系统线程
  • 是 OS 创建的
  • 真正跑 CPU 的是 M

3️⃣ P —— Processor(最关键)

P 是整个模型的核心。

P 是:

调度器的资源控制单元

它负责:

  • 管理本地 G 队列
  • 把 G 分配给 M 执行
  • 维护调度状态

可以理解为:

M 想干活,必须拿到 P


四、三者关系图

一个简化关系:

G → 被放入 → P 的本地队列
M → 必须绑定 → P
M + P → 才能执行 G

执行流程:

1. 创建 G
2. G 放入某个 P 的队列
3. M 绑定 P
4. M 从 P 队列取 G
5. 执行 G

五、为什么要有 P?

很多人问:

为什么不直接 G + M?

因为:

如果只有 G 和 M:

  • 所有 goroutine 共享一个全局队列
  • 频繁加锁
  • 性能差

加入 P 之后:

每个 P 有:

  • 本地 G 队列(减少锁竞争)
  • 运行中的 G

优点:

  • 降低锁竞争
  • 提高 CPU 利用率
  • 更好扩展到多核

六、数量关系

默认情况下:

P 的数量 = CPU 核心数

可以通过设置:

runtime.GOMAXPROCS(n)

来修改 P 的数量。


七、完整调度过程(一步一步)

假设有 4 核 CPU:

P0  P1  P2  P3

1️⃣ 创建 goroutine
→ 放进某个 P 的本地队列

2️⃣ M 绑定 P
→ 每个 P 同时只能被一个 M 使用

3️⃣ M 从 P 队列取 G
→ 执行

4️⃣ 如果队列空了?
→ 去别的 P 偷任务(Work Stealing)


八、Work Stealing(工作窃取)

这是 Go 调度器非常重要的机制。

如果:

P0 很忙
P1 没活干

P1 会:

从 P0 的队列“偷一半任务”过来

这样可以:

  • 保证负载均衡
  • 提高 CPU 利用率

九、阻塞时会发生什么?

比如:

time.Sleep()
网络 IO
channel 阻塞

当 G 阻塞时:

  1. M 不会一直等
  2. M 会解绑当前 P
  3. P 会找别的 M 继续干活

这样不会浪费 CPU。


十、总结一句话理解 GMP

可以记住这个公式:

G 是任务
M 是执行者
P 是调度资源
M 必须拿到 P 才能执行 G

再简化:

G = 干什么
M = 谁干
P = 批准干 + 排队系统

十一、面试级总结版本

如果你以后面试,可以这样回答:

Go 使用 GMP 调度模型。

G 表示 goroutine,是待执行的任务。
M 是操作系统线程,真正执行代码。
P 是调度资源,维护本地 goroutine 队列。

M 必须绑定 P 才能执行 G。

Go 通过本地队列 + 工作窃取算法,实现高效并发调度。


十二、给编程小白的最终理解图

你可以在脑中想象:

CPU核心数量 = P数量
每个P都有一个任务仓库
M是工人
G是任务
工人必须拿到一个仓库许可(P)
才能从仓库拿任务干活
http://www.jsqmd.com/news/355917/

相关文章:

  • Redis数据类型的底层实现和数据持久化
  • 基于机器学习的眼底图像糖尿病视网膜病变诊断系统-大数据深度学习算法毕设毕业设计项目PyQt
  • 基于卷积神经网络的地震数据破碎带识别方法研究-大数据深度学习算法毕设毕业设计项目PyQT
  • P2367 语文成绩
  • 2026年国内超声波振动筛厂家全景盘点及实力解析 - damaigeo
  • 2026年2月大型活动发电机租赁公司最新推荐,定制化供电方案与现场保障优选榜 - 品牌鉴赏师
  • 【无标题】vlan
  • 深入浅出:使用Linux系统函数构建高性能TCP服务器
  • 生发机构哪个更有效?黑奥秘AI智能检测,千人千方更高效
  • AI元人文构想理论体系全球思想学术综述
  • 2026年2月木模板厂家最新推荐,权威测评与实木板材品质把控指南 - 品牌鉴赏师
  • 我电脑上安装了androidstudio 但是我不知道adb 安装在哪个路径下,如何找到 windows11
  • Qt 帮助文档为空问题(查询无结果)
  • 深入解析:串口无线数传模块:实现汽车零部件厂房 PLC 与触摸屏 300 米稳定交互
  • 超声波振动筛厂家怎么选?核心标准+标杆品牌推荐 - damaigeo
  • 斑马优化算法优化BP神经网络风电功率预测附Matlab代码
  • 【Hadoop+Spark+python毕设】基于大数据的车辆二氧化碳排放量可视化分析系统、计算机毕业设计、包括数据爬取、数据分析、数据可视化、实战教学
  • 青岛华硕笔记本维修点_青岛华硕电脑售后服务网点查询 - damaigeo
  • 第七日笔记
  • 深浅拷贝、STL迭代器失效 - 教程
  • 史上最大规模CNV研究:47万人全基因组测序揭示疾病新线索
  • Java面向对象——抽象类
  • 生发养发馆哪家效果好?黑奥秘三位一体理念+标准化服务保障品质
  • 2/7
  • BUUCTF刷题MISC[九] (89-96)
  • Cherry接入本地自定义mcpService记录
  • 从零开始构建AI原生应用:API编排最佳实践
  • 青岛华硕电脑维修站-青岛华硕服务 - damaigeo
  • 巴菲特的复利效应运用
  • 华盛顿大学APRIL数据集:AI从编译器错误中学习数学证明修复