Go--GMP调度模型

news2024/9/23 9:34:55

目录

  • GMP模型
    • G、M、P简介
    • P和M的个数
    • **P和M何时会被创建**
    • goroutine创建流程
    • goroutine什么时候会被挂起
  • GMP的调度
    • 调度流程
    • 调度策略
    • 调度时机
    • 同时启动了一万个goroutine,会如何调度?

GMP模型

G、M、P简介

GMP是Go运行时调度层面的实现,包含4个重要结构,分别是G、M、P、Sched
在这里插入图片描述

  • G(Goroutine):代表Go 协程Goroutine,存储了 Goroutine 的执行栈信息、Goroutine 状态以及Goroutine 的任务函数等。G的数量无限制,理论上只受内存的影响,创建一个 G 的初始栈大小为2-4K,配置一般的机器也能简简单单开启数十万个 Goroutine ,而且Go语言在 G 退出的时候还会把 G 清理之后放到 P本地或者全局的闲置列表 gFree 中以便复用。
  • M(Machine): Go 对操作系统线程(OS thread)的封装,可以看作操作系统内核线程,想要在 CPU 上执行代码必须有线程,通过系统调用 clone 创建。M在绑定有效的 P 后,进入一个调度循环,而调度循环的机制大致是从 P 的本地运行队列以及全局队列中获取 G,切换到 G 的执行栈上并执行 G 的函数,调用 goexit 做清理工作并回到 M,如此反复。M 并不保留 G 状态,这是 G 可以跨 M 调度的基础。M的数量有限制,默认数量限制是 10000,可以通过 debug.SetMaxThreads() 方法进行设置,如果有M空闲,那么就会回收或者睡眠。
  • **P(Processor):虚拟处理器,M执行G所需要的资源和上下文,只有将 P 和 M 绑定,才能让 P 的 runq 中的 G 真正运行起来。P 的数量决定了系统内最大可并行的 G 的数量,**P的数量受本机的CPU核数影响,可通过环境变量$GOMAXPROCS或在runtime.GOMAXPROCS()来设置,默认为CPU核心数。
  • Sched:调度器结构,它维护有存储M和G的全局队列,以及调度器的一些状态信息

P和M的个数

  • P: 由启动时环境变量 $GOMAXPROCS 或者是由 runtime的方法GOMAXPROCS()决定。这意味着在程序执行的任意时刻都只有$GOMAXPROCS个goroutine在同时运行。
  • M:
    • Go 语言本身的限制:Go 程序启动时,会设置 M 的最大数量,默认 10000,但是内核很难支持这么多的线程数,所以这个限制可以忽略。
    • runtime/debug 中的 SetMaxThreads 函数,设置 M 的最大数量。
    • 一个 M 阻塞了,会创建新的 M。

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

P和M何时会被创建

P: 在确定了 P 的最大数量 n 后,运行时系统会根据这个数量创建 n 个 P。

M: 没有足够的 M 来关联 P 并运行其中的可运行的 G 时创建。比如所有的 M 此时都阻塞住了,而 P 中还有很多就绪任务,就会去寻找空闲的 M,而没有空闲的,就会去创建新的 M。

goroutine创建流程

在调用go func()的时候,会调用runtime.newproc来创建一个goroutine,这个goroutine会新建一个自己的栈空间,同时在G的sched中维护栈地址与程序计数器这些信息(备注:这些数据在goroutine被调度的时候会被用到。准确的说该goroutine在放弃cpu之后,下一次在重新获取cpu的时候,这些信息会被重新加载到cpu的寄存器中。)

创建好的这个goroutine会被放到它所对应的内核线程M所使用的上下文P中的run_queue中,等待调度器来决定何时取出该goroutine并执行,通常调度是按时间顺序被调度的,这个队列是一个先进先出的队列

goroutine什么时候会被挂起

  • waitReasonChanReceiveNilChan:对未初始化的 channel 进行读操作
  • waitReasonChanSendNilChan:对未初始化的 channel 进行写操作
  • 在 main goroutine 发生 panic 时,会触发
  • 在调用关键字 select 时会触发
  • 在调用关键字 select 时,若一个 case 都没有,会直接触发
  • 在 channel 进行读操作,会触发
  • 在 channel 进行写操作,会触发
  • sleep 行为,会触发
  • IO 阻塞等待时,例如:网络请求等
  • 在垃圾回收时,主要场景是 GC 标记终止和标记阶段时触发
  • GC 清扫阶段中的结束行为,会触发
  • 信号量处理结束时,会触发

GMP的调度

调度流程

在这里插入图片描述

  • 每个 P 有个局部队列,局部队列保存待执行的goroutine(流程 2),当 M 绑定的 P 的的局部队列已经满了之后就会把 goroutine 放到全局队列(流 程 2-1)
  • 每个 P 和一个 M 绑定,M 是真正的执行 P 中 goroutine 的实体(流程 3), M 从绑定的 P 中的局部队列获取 G 来执行
  • 当 M 绑定的 P 的局部队列为空时,M 会从全局队列获取到本地队列来执行 G (流程 3.1),当从全局队列中没有获取到可执行的 G 时候,M 会从其他 P 的局部队列中偷取 G 来执行(流程 3.2),这种从其他 P 偷的方式称为 work stealing
  • 当 G 因系统调用(syscall)阻塞时会阻塞 M,此时 P 会和 M 解绑即 hand off,并寻找新的 idle 的 M,若没有 idle 的 M 就会新建一个 M(流程 5.1)
  • 当 G 因 channel 或者 network I/O 阻塞时,不会阻塞 M,M 会寻找其他 runnable 的 G;当阻塞的 G 恢复后会重新进入 runnable 进入 P 队列等待执 行(流程 5.3)

调度策略

由于 P 中的 G 分布在 runnext、本地队列、全局队列、网络轮询器中,则需要挨个判断是否有可执行的 G,大体逻辑如下:

  • 每执行61次调度循环,从全局队列获取G,若有则直接返回
  • 从P 上的 runnext 看一下是否有 G,若有则直接返回
  • 从P 上的 本地队列 看一下是否有 G,若有则直接返回
  • 上面都没查找到时,则去全局队列、网络轮询器查找或者从其他 P 中窃取,一直阻塞直到获取到一个可用的 G 为止

调度时机

什么时候进行调度(执行/切换)?

在以下情形下,会切换正在执行的goroutine

  • 抢占式调度
    • sysmon 检测到协程运行过久(比如sleep,死循环)
      • 切换到g0,进入调度循环
  • 主动调度
    • 新起一个协程和协程执行完毕
      • 触发调度循环
    • 主动调用runtime.Gosched()
      • 切换到g0,进入调度循环
    • 垃圾回收之后
      • stw之后,会重新选择g开始执行
  • 被动调度
    • 系统调用(比如文件IO)阻塞(同步)
      • 阻塞G和M,P与M分离,将P交给其它M绑定,其它M执行P的剩余G
    • 网络IO调用阻塞(异步)
      • 阻塞G,G移动到NetPoller,M执行P的剩余G
    • atomic/mutex/channel等阻塞(异步)
      • 阻塞G,G移动到channel的等待队列中,M执行P的剩余G

同时启动了一万个goroutine,会如何调度?

一万个G会按照P的设定个数,尽量平均地分配到每个P的本地队列中。如果所有本地队列都满了,那么剩余的G则会分配到GMP的全局队列上。接下来便开始执行GMP模型的调度策略:

  • 本地队列轮转:每个P维护着一个包含G的队列,不考虑G进入系统调用或IO操作的情况下,P周期性的将G调度到M中执行,执行一小段时间,将上下文保存下来,然后将G放到队列尾部,然后从队首中重新取出一个G进行调度。
  • 系统调用:P的个数默认等于CPU核数,每个M必须持有一个P才可以执行G,一般情况下M的个数会略大于P的个数,这多出来的M将会在G产生系统调用时发挥作用。当该G即将进入系统调用时,对应的M由于陷入系统调用而进被阻塞,将释放P,进而某个空闲的M1获取P,继续执行P队列中剩下的G。
  • 工作量窃取:多个P中维护的G队列有可能是不均衡的,当某个P已经将G全部执行完,然后去查询全局队列,全局队列中也没有新的G,而另一个M中队列中还有3很多G待运行。此时,空闲的P会将其他P中的G偷取一部分过来,一般每次偷取一半。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2036758.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

质量对中国开发商提升游戏品牌信誉和信任度的影响

随着全球游戏产业的持续增长,中国开发商正在大举进军国际市场。然而,他们面临的关键挑战之一是建立和维护与全球参与者的品牌信誉和信任。他们的游戏质量在实现这一目标方面起着至关重要的作用。从技术性能到故事讲述和本地化,高质量的游戏对…

OpenGL3.3_C++_Windows(35)

PBR_IBL漫反射 IBL图像的光照(Image based lighting):非直接光源,它是一种更精确的环境光照输入格式,甚至也可以说是一种全局光照的粗略近似。环境光照:获取每个wi光源辐射率,求辐照度:将周围环…

Linux学习笔记11(计算机网络)

目录 网络七层模型/五层模型 IP地址分类 CIDR Centos7的网卡IP配置 RockyLinux9的网卡IP配置 网络七层模型/五层模型 自下到上 物理层: 建立物理连接,传输 0 和 1 的比特流 数据链路层: 物理地址寻址,流量控制,差错…

基于vue框架的SSM基于B_S的毕业设计题目管理系统的设计与实现ij0q7(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能:学生,教师,毕设题目,毕设选题,毕设任务书,开题报告,中期检查,毕业论文,论文成绩,答辩成绩,答辩通知,班级 开题报告内容 基于Vue框架的SSM(SpringSpring MVCMyBatis)的毕业设计题目管理系统设计与实现 开题报告…

刷题记录第109天-K个一组反转链表

解题思路: 第一步:实现一个数组,给定一段链表的头结点和尾节点,反转该链表,并返回新的头结点和尾结点。 第二步:初始化一个虚拟头结点,用于记录最终头结点和规范操作。 第三步:给定一…

​产品经理-​你如何理解“互联网思维(35)

在产品规划和功能改版中,确实非常重视用户需求和体验。产品需求是互联网产品的核心 用户体验是互联网产品的重点。在互联网新产品规划中,会非常重视用户验证环节 确保做出来的东西确实是用户想要的;而在已经上线的产品中,往往会有…

Raspberry Pi Pico 家族的进化 —— RP2040、RP2350与RP2354性能比较

随着树莓派Pico系列的不断扩展,其背后的芯片也得到了升级和改进。从最初的RP2040到最新的RP2354,每一次迭代都带来了新的功能和性能提升。本文将详细对比RP2040、RP2350和RP2354三款芯片的关键特性,帮助开发者了解它们的差异,并选…

Windows File Recovery卡在99%怎么解决?实用指南!

为什么会出现“Windows File Recovery卡在99%”的问题? Windows File Recovery(Windows文件恢复)是微软设计的命令行应用程序。它可以帮助用户从健康/损坏/格式化的存储设备中恢复已删除/丢失的文件。 通过输入相关命令,设置源/…

【轨物推荐】技术创新的演进逻辑

注:本文节选自郭朝晖老师的《知行-工业基因的数字化演进》 推进数字化技术的过程本质上是一种技术创新。理解创新的逻辑,有利于推进数字化技术。 科技工作的价值往往短期被高估,长期被低估。短期被高估,是因为忽视了技术应用过程…

LSPosed模块开发第一篇

安装LSPosed 设备pixel 3a Android 12 Magisk root 环境 LSPosed地址: https://github.com/LSPosed/LSPosed 下载zygisk的,riru没效果 https://github.com/LSPosed/LSPosed/releases 下载完push 到手机,Magisk 安装模块 Magisk设置里面的Z…

【人工智能】【机器学习】- 好书推荐之《深度学习调参指南》

目录 目标读者 核心内容 特色 结构 《深度学习调参指南》是一份由Google和哈佛大学的研究人员与工程师共同编写的实战手册,旨在帮助读者系统性地优化深度学习模型的性能。该指南强调了在深度学习实践中遇到的实际问题和解决方案,尤其关注超参数调优的…

go-zero结合自定义模版校验前端参数

一、自定义模版的使用 如果想对官网goctl命名生成的项目结构改变的话,可以使用模版,自定义模版,然后生成自己想要的文件 1、使用命令将官方模版映射到本地 goctl template init2、在项目的根目录下添加文件夹,把刚刚映射到本地的拷贝到项目中 3、使用模版根据api文…

luckyexcel 编辑预览excel文件

luckyexcel 编辑预览excel文件 支持后端传文件流预览编辑&#xff0c;也支持选择本地文件编辑预览 看效果 上代码 <template><div style"margin: 30px"><div class"button-box2"><div><div style"color: red">…

【精通SDL之----SDL_RenderReadPixels截屏】

SDL_RenderReadPixels截屏 前言一、SDL_RenderReadPixels简介二、问题现象三、规避方案1. 离屏纹理2. ding! *灵光一现* 前言 最近使用SDL2在鸿蒙系统(Harmoney OS)上截取视频播放过程中的数据&#xff0c;发现捕获的数据为空&#xff0c;然在windows上却可以正常捕获&#xff…

Linux 内核源码分析---网络层分析

版本(version)&#xff1a;指定所用 IP 协议的版本&#xff0c;该字段有效值为 4 或 6&#xff1b; IP首部长度(IHL)&#xff1a;定义首部的长度&#xff0c;由于选项数量可变&#xff1b; 服务区分&#xff1a;用于更复杂协议选项&#xff1b; 长度&#xff1a;指定分组的总长…

AI技术重塑招聘流程

一、引言 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;技术正以前所未有的速度渗透到社会的各个领域&#xff0c;其中&#xff0c;人力资源管理领域也不例外。在全员招聘这一关键环节中&#xff0c;AI技术的应用不仅极大地提高了招聘效率&#xff0c;还…

【Java数据结构】---Queue

乐观学习&#xff0c;乐观生活&#xff0c;才能不断前进啊&#xff01;&#xff01;&#xff01; 我的主页&#xff1a;optimistic_chen 我的专栏&#xff1a;c语言 &#xff0c;Java 欢迎大家访问~ 创作不易&#xff0c;大佬们点赞鼓励下吧~ 文章目录 前言队列Queue队列的模拟…

fme处理空间数据入门v0

概述 本教程是最最最基本的fme处理空间数据内容&#xff0c;给新同事介绍我以前是怎么干活用的&#xff0c;基本啥有用的东西。 本教程主要说如何在 FME Workbench中创建模板文件&#xff08;一般我们把fme写的工作空间就叫模板&#xff09;&#xff0c;教程里面主要是利用GI…

Linux2.6内核进程调度队列详细讲解

上图是 Linux2.6 内核中进程队列的数据结构&#xff0c;之间关系也已经给大家画出来&#xff0c;方便大家理解。 一个 CPU 拥有一个 runqueue。 Linux真正的调度方式是通过runqueue进行调度的&#xff0c;我们知道进程的优先级范围是根据nice值确定的&#xff0c;而nice值的范围…

怎么配置Datagrip中字体的大小,修改注释的颜色呢

新手刚安装Datagrip时&#xff0c;都会遇到怎么调字体大小的问题&#xff0c;接下来解决一下吧 具体步骤&#xff1a; 1.进入Datagrip的操作窗口后&#xff0c;点击左上方的菜单栏中file(文件)。 2.在文件中点击设置setting。 3.在设置窗口点击Editor,再点击Font。 4.在Fo…