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

图解GMP模型

在Go中.线程是运行Goroutine的实体.调度器的功能是把可运行的Goroutine分配到工作线程上.

在GMP模型有以下重要的概念.如图所示.

1).全局队列(Global Queue):存放等待运行的G.全局队列可能被任意的P去获取里面的G.所以全局队列相当于整个模型中的全局资源.那么自然对于队列的读写操作是要加入互斥动作的.

2).P的本地队列:同全局队列类似.存放的也是等待运行的G.但存放的数量有限.不超过256个.新建G时.G优先加入P的本地队列.如果队列满了.则会把本地队列的一半的G移动到全局队列.

3).P列表:所有的P都在程序启动时创建.并保存在数组中.最多有GOMAXPROCS(可配置)个.

4).M:线程想运行任务就得获取P.从P的本地队列获取G.当P队列为空时.M也会尝试从全局队列获得一批G放到P的本地队列.或从其他P的本地队列"偷"一半放到自己P的本地队列.M运行G.G执行后.M会从P获取下一个G.不断重复下去.Goroutine调度器和OS调度是通过M结合起来的.每个M都代表了一个内核线程.OS调度器负责把内核线程分配到CPU的核上执行.

P和M的个数问题:

1).P的数量由启动时环境变量GOMAXPROCS或者由runtime的方法GOMAXPROCS()决定.这意味着在程序执行的任意时刻都只有GOMAXPROCS个Goroutine在同时执行.

2).M的数量由Go语言本身的限制决定.Go程序启动时会设置M的最大数量.默认为10000个.但是内核很难支持这么多线程数.所以这个限制可以忽略.runtime/deBug中的SetMaxThreads()函数可设置M的最大数量.当一个M阻塞了时会创建新的M.

M与P的数量没有绝对关系.一个M阻塞.P就会创建或者切换另一个M.所以.即使P的默认数量是1.也有可能会创建很多M出来.

P和M何时被创建:

1).P创建的时机在确定了P的最大数量n后.运行时系统会根据这个数量创建n个P.

2).M创建的时机是在当没有足够的M来关联P并运行其中可运行的G的时候.例如所有的M此时都阻塞住了.而P中还有很多就绪任务.就会寻找空闲的M.如果没有空闲的M就会创建新的M.

调度器设计策略:

策略1:复用线程

避免频繁的创建 销毁线程.而是对线程的复用.

1).偷取(Work Stealing)机制:

当本线程无可运行的的G时.尝试从其他线程绑定的P偷取G.而不是销毁线程.如图所示.

这里需要注意的是.偷取的动作一定是由P发起的.而非M.因为P的数量是固定的.如果一个M得不到一个P.那么这个M是没有执行的本地队列.更谈不上其他的P队列偷取.

2).移交(Hand Off)机制:

当本线程因为G进行系统调用阻塞时.线程会释放绑定的P.把P转移给其他空闲的线程执行.如图所示.

此时若在M1的GPM组合中.G正在被调度.并且发生了阻塞.则这个时候就会触发移交的设计机制.GPM模型为了更大程度的利用M和P的性能.不会让一个P永远被一个阻塞的G耽误后续的工作.遇到这种情况的时候.移交机制的设计理念应该立刻此时的P释放出来.

为了释放P.所以将P和M1 G1分离.M1由于正在执行当前的G1.全部程序栈空间均在M1中保存.所以M1此时应该与G1一同进入阻塞状态.但是已经释放的P需要跟另一个M进行绑定.所以就会选择一个M3(如果此时没有M3.则会创建一个新的或者唤醒一个正在睡眠的M)进行绑定.这样新的P就会继续工作.接收新的G或者其他的队列中实施偷取机制.

2.)策略二:利用并行

GOMAXPROCS设置P的数量.最多有COMAXPROCS个线程分布在多个CPU上同时运行.GOMAXPROCS也限制了并发的程度.例如.GOMAXPROCS=核数/2.表示最多利用一半的CPU核进行并行.

3).策略三:抢占

在Co-routine中要等待一个协程主动让出CPU才执行下一个协程.在Go中.一个Goroutine最多占用CPU10ms.防止其他Goroutine无资源可用.这就是Goroutine不同于Co-routine的一个地方.如图所示.

策略4:全局G队列

在新的调度器中依然有全局G队列.但功能已经被弱化了.当M执行偷取.但从其他地方偷取不到G时.它可以从全局G队列获取G.如图所示.

go func()调度流程:

1).通过go func()创建一个Goroutine.如图所示.

2).有两个存储G的队列.一个是局部调度器P的本地队列.另一个是全局G队列.新创建的G会先保存在P的本地队列中.如果P的本地队列已经满了.就会保存到全局队列中.如果所示.

3).G只能运行在M中.一个M必须持有一个P.M与P是1:1的关系.M会从P的本地队列弹出一个可执行状态的G来执行.如果P的本地队列为空.则会从全局队列进行获取.如果从全局队列获取不到.则会向其他的MP组合偷取一个可执行的G来执行.如图所示.

4).一个M调度G执行的过程是一个循环机制.如图所示.

5).当M执行某一个G时.如果发生了syscall或者其余阻塞操作.则M会阻塞.如果.当前有一些G在执行.runtime则会把这个线程M从P中移除.然后创建一个新的操作系统线程(如果有空闲的线程可用就复用空闲线程)来服务于这个P.如图所示.

6).当M系统调用结束时.这个G会尝试获取一个空闲的P执行.并放入这个P的本地队列.如果获取不到P.则这个线程M会变成休眠状态.加入空闲线程中.然后这个G会被放入全局队列中.如图所示.

调度器的生命周期:

在Go语言调度器的GPM模型还有两个比较特殊的角色.它们分别是M0和G0.

1.M0:

1).启动程序后的编号为0的主线程.

2).在全局命令runtime.m0中,不需要再heap堆上分配.

3).负责执行初始化操作和启动第一个G.

4).启动第一个G后.M0就和其他M一样了.

2.G0:

1).每次启动一个M.创建的第一个Goroutine就是G0.

2).G0仅用于负责调度G.

3).G0不指向任何可执行的函数.

4).每个M都会有一个自己的G0.

5).在调度或系统调度时.会使用M切换到G0.在通过G0调度.

6).M0的G0会放在全局空间.

一个Goroutine的创建周期如果加上M0和G0的角色.具体流程如下.

麦浪翻晴风飐柳,已过伤春候。因甚为他成僝僽?毕竟是春迤逗。

红药阑边携素手,暖语浓于酒。盼到园花铺似绣,却更比春前瘦。 纳兰

语雀地址https://www.yuque.com/itbosunmianyi/xg8vfe?

《Go.》 密码:xbkk 欢迎大家访问.提意见.

如果大家喜欢我的分享的话.可以关注我的微信公众号

念何架构之路

http://www.jsqmd.com/news/599853/

相关文章:

  • 零基础易上手的数据分析工具:Wyn 商业智能软件
  • 不止于流水灯:用WS2812B和51单片机打造你的第一个智能氛围灯项目(含呼吸、渐变、流星效果源码)
  • 测试小白福音:在快马上通过实战代码轻松攻克软件测试面试题
  • python基于大数据的食谱分析与个性化推荐系统
  • 【需求改变与测试如何】
  • OpenClaw安全加固:Phi-3-vision服务接口的权限控制实践
  • Mac M芯片适配:OpenClaw调用Qwen3-14B镜像的ARM环境配置
  • 数据结构 | 单链表
  • 2026奉化考试提分机构推荐榜:临安考试提分/临平考试提分/义乌考试提分/乐清考试提分/仙居考试提分/选择指南 - 优质品牌商家
  • Simulink仿真:基于开关电容的电池均衡
  • 成都定制抽纸高性价比厂家推荐榜:酒店餐饮用品定做/餐厅用纸/商务抽纸盒/商用卫生纸/定制logo商务纸巾/选择指南 - 优质品牌商家
  • 论文精读:突破大模型推理瓶颈:为什么“限制自信”反而能让 AI 更聪明?
  • OpenClaw智能错题本:Qwen3.5-9B整理LeetCode错误并生成专项练习
  • 永磁同步电机PMSM无感FOC驱动代码功能说明
  • 半导体年会推荐:精选行业高端年会搭建交流合作共赢优质平台 - 品牌2026
  • R语言处理JSON文件的方法详解
  • 如何高效使用付费墙绕过工具:Chrome扩展的完整实践指南
  • OpenClaw任务编排技巧:SecGPT-14B多步骤安全审计流水线
  • Zigbee楼宇环境监测系统设计与实现
  • 2026年可靠企业同城送水品牌推荐榜:家庭订桶装水/怡宝桶装水配送/成都同城送水/景田桶装水配送/杭州同城送水/选择指南 - 优质品牌商家
  • 深圳SEO网站优化公司有哪些客户评价
  • COMSOL仿真石墨烯吸收器,带视频演示,一步一步教学,原文章来自于一篇二区文章。 图片展示为...
  • obsidian claudian 插件配置使用minimax模型
  • Cline与大模型的交互协议(内涵Agent实现原理)
  • 【超详细】步进电机选型避坑指南:这5个参数没搞懂,买回来就是废铁
  • 永磁同步电机PMSM无感FOC控制:扩展卡尔曼滤波器EKF观测器,代码运行无错,支持无感启动...
  • 新手福音:用快马AI生成三极管工作原理交互式学习工具
  • OpenClaw报错大全:Qwen3-32B镜像部署常见问题与解决
  • 实战演练:基于Next.js与快马AI接口,构建可交互的qoderwork官网演示版
  • OpenClaw+千问3.5-9B:个人知识库自动分类归档