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

深入 Go 底层原理(四):GMP 模型深度解析

1. 引言

在上一篇文章中,我们宏观地了解了 Go 的调度策略。现在,我们将深入到构成这个调度系统的三大核心组件:GMP。理解 GMP 模型是彻底搞懂 Go 并发调度原理的关键。

本文将详细解析 G、M、P 各自的职责以及它们之间是如何协同工作的。

2. GMP 核心组件
  • G (Goroutine):

    • 定义:G 就是我们常说的 Goroutine。它是一个待执行的任务单元,包含了执行所需的栈空间指令指针 (PC) 以及其他状态信息(如 goroutine status)。

    • 特点:G 是轻量级的,初始栈大小仅为 2KB,可以根据需要动态伸缩。Go 程序可以轻松创建成千上万个 G。

    • 状态:G 有多种状态,如 _Gidle (闲置), _Grunnable (可运行), _Grunning (运行中), _Gsyscall (系统调用中), _Gwaiting (等待中), _Gdead (已死亡) 等。调度器根据这些状态来移动 G。

  • M (Machine):

    • 定义:M 是内核线程的抽象,是真正执行代码的实体。runtime 会限制 M 的数量,默认不超过 10000。

    • 职责:M 从一个关联的 P 的本地队列中获取 G,然后执行 G 的代码。如果 G 发生系统调用或阻塞,M 可能会与 P 解绑。

    • M0MM0是主线程,是程序启动时创建的第一个内核线程。

  • P (Processor):

    • 定义:P 是处理器的抽象,它代表了 M 执行 Go 代码所需的上下文资源。P 的数量在程序启动时被设置为 GOMAXPROCS 的值,通常等于 CPU 的核心数。

    • 职责:P 是 G 和 M 之间的“中间人”。它维护一个本地可运行 G 队列 (LRQ),为 M 提供可执行的 G。P 的存在使得调度器可以控制并发的程度(即同时有多少个 G 在真正地运行)。

    • 关键作用:P 的引入实现了 M 和 G 的解耦。当一个 M 因为其上运行的 G 进行系统调用而阻塞时,P 会从这个 M 上解绑,并去寻找一个空闲的 M(或创建一个新的 M)来继续执行自己队列中的其他 G。这使得 Go 的并发能力不会因为部分 Goroutine 的阻塞而受限。

3. GMP 的协作流程

一个典型的调度场景如下:

  1. 启动:程序启动,创建 M0,并创建 GOMAXPROCS 个 P。

  2. G 的创建go func() 创建一个新的 G。这个新的 G 会被优先放入当前 M 绑定的 P 的本地队列 (LRQ) 的队头。

  3. M 的执行循环

    • M 启动后,会绑定一个 P(acquirep)。

    • M 进入一个无限的调度循环 (schedule)。

    • 在循环中,M 尝试从 P 的 LRQ、全局队列 (GRQ) 或通过工作窃取 (runqsteal) 来获取一个可运行的 G。

    • 找到 G 后,M 会执行 G 的代码 (execute)。

    • G 执行完毕后,M 会再次进入调度循环寻找下一个 G。

  4. 系统调用场景 (Syscall):

    • 假设 M0 上的 G0 准备进行一个会阻塞的系统调用。

    • M0 会与它的 P0 解绑

    • P0 会去寻找一个空闲的 M(比如 M1)或者创建一个新的 M1。

    • P0 会绑定到 M1 上,并继续执行 P0 本地队列中的其他 G。

    • 与此同时,原来的 M0 则陷入系统调用,等待其完成。

    • 当 G0 的系统调用结束后,它会尝试获取一个空闲的 P 来继续执行。如果获取不到,G0 会被放入全局队列。M0 则会进入休眠。

这个设计使得 P(并发的许可证)不会因为 M 的阻塞而被浪费,从而保证了 GOMAXPROCS 个 Goroutine 可以持续地、并行地运行。

4. sysmon 监控线程

除了 G, M, P,还有一个重要的后台角色:sysmon。它是一个由 runtime 启动的、不需要 P 的 M,在后台执行以下任务:

  • 抢占:如上篇所述,向运行时间过长的 G 发送抢占信号。

  • 网络轮询 (netpoller):检查网络 I/O 是否就绪,并唤醒因此阻塞的 G。

  • GC 辅助:在需要时触发 GC。

  • 释放闲置资源:定期将长时间空闲的 M 和 P 的资源回收。

5. 总结

GMP 模型是 Go 语言高性能并发调度的核心。

  • G 是任务单元。

  • M 是执行实体(内核线程)。

  • P 是调度上下文和资源的提供者,是实现 M:N 模型的关键。

它们通过工作窃取、系统调用处理、异步抢占等机制紧密协作,构成了一个强大、高效、自适应的调度系统。

http://www.dtcms.com/a/311924.html

相关文章:

  • 编译器与解释器:核心原理与工程实践
  • Linux I/O 系统调用完整对比分析
  • linux source命令使用详细介绍
  • [qt]QTreeWidget使用
  • JAVA国际版同城服务同城信息同城任务发布平台APP源码Android + IOS
  • 【设计模式】 原则
  • AI驱动SEO关键词智能进化
  • 具身智能VLA困于“数据泥潭”,人类活动视频数据是否是“破局之钥”?
  • 【Python修仙编程】(二) Python3灵源初探(10)
  • Spring 全局异常处理机制:多个 @ControllerAdvice 与重复 @ExceptionHandler
  • CMake 命令行参数完全指南 (1)
  • 数据结构1-概要、单向链表
  • JVM中的垃圾回收暂停是什么,为什么会出现暂停,不同的垃圾回收机制暂停对比
  • 知识随记-----用 Qt 打造优雅的密码输入框:添加右侧眼睛图标切换显示
  • Ubuntu系统间SSH控制详细指南
  • 由浅入深使用LangGraph创建一个Agent工作流
  • 零拷贝技术:高效数据传输的核心原理与应用
  • 用 JavaSwing 开发经典横版射击游戏:从 0 到 1 实现简易 Contra-like 游戏
  • 20250801-2-Kubernetes 存储-节点本地数据卷_笔记
  • IMAP电子邮件归档系统Mail-Archiver
  • UE5 Insight ProfileCPU
  • 自动驾驶嵌入式软件工程师面试题【持续更新】
  • 回归预测 | Matlab实现CNN-LSTM-self-Attention多变量回归预测
  • Java中的字符串 - String 类
  • 编程与数学 03-002 计算机网络 19_网络新技术研究
  • Java试题-选择题(6)
  • 苏州银行招苏新基金研究部研究员
  • python匿名函数lambda
  • Windows Server软件限制策略(SRP)配置
  • linux进度条程序