侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

go语言中的GMP模型和GM模型浅析介绍

Go  /  管理员 发布于 3星期前   241

G: Goroutine

代表Go 协程Goroutine,存储了 Goroutine 的执行栈信息、Goroutine 状态以及 Goroutine 的任务函数等。G的数量无限制,理论上只受内存的影响,创建一个 G 的初始栈大小为2-4K,配置一般的机器也能简简单单开启数十万个 Goroutine ,而且Go语言在 G 退出的时候还会把 G 清理之后放到 P 本地或者全局的闲置列表 gFree 中以便复用。


M: 线程

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的全局队列,以及调度器的一些状态信息


一.GM模型:

2012年前的调度器模型,使用了4年果断被抛弃,缺点如下:

1.  创建、销毁、调度G都需要每个M获取锁,这就形成了激烈的锁竞争。

2.  M转移G会造成延迟和额外的系统负载。

     比如当G中包含创建新协程的时候,M创建了G’,为了继续执行G,需要把G’交给M’执行,也造       成了很差的局部性,因为G’和G是相关的,最好放在M上执行,而不是其他M'。

3.  系统调用(CPU在M之间的切换)导致频繁的线程阻塞和取消阻塞操作增加了系统开销。


二.GMP模型:

P的数量:

由启动时环境变量`$GOMAXPROCS`或者是由`runtime`的方法`GOMAXPROCS()`决定


M的数量:

go语言本身的限制:

go程序启动时,会设置M的最大数量,默认10000.但是内核很难支持这么多的线程数

runtime/debug中的SetMaxThreads函数,设置M的最大数量

一个M阻塞了,会创建新的M。


P何时创建:

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


M何时创建:

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


全场景解析:

1.P拥有G1,M1获取P后开始运行G1,G1创建了G2,为了局部性G2优先加入到P1的本地队列。

2.G1运行完成后,M上运行的goroutine切换为G0,G0负责调度时协程的切换。
 从P的本地队列取G2,从G0切换到G2,并开始运行G2。实现了线程M1的复用。

3.假设每个P的本地队列只能存4个G。
 G2要创建了6个G,前4个G(G3, G4, G5, G6)已经加入p1的本地队列,p1本地队列满了。

4.G2在创建G7的时候,发现P1的本地队列已满,
 需要执行负载均衡(把P1中本地队列中前一半的G,还有新创建G转移到全局队列),
 这些G被转移到全局队列时,会被打乱顺序

5.G2创建G8时,P1的本地队列未满,所以G8会被加入到P1的本地队列。

6.在创建G时,运行的G会尝试唤醒其他空闲的P和M组合去执行。
 假定G2唤醒了M2,M2绑定了P2,并运行G0,但P2本地队列没有G,M2此时为自旋线程

7.M2尝试从全局队列取一批G放到P2的本地队列,至少从全局队列取1个g,
 但每次不要从全局队列移动太多的g到p本地队列,给其他p留点。

8.假设G2一直在M1上运行,经过2轮后,M2已经把G7、G4从全局队列获取到了P2的本地队列
 并完成运行,全局队列和P2的本地队列都空了,那m就要执行work stealing(偷取):
 从其他有G的P哪里偷取一半G过来,放到自己的P本地队列。P2从P1的本地队列尾部取一半的G

9.G1本地队列G5、G6已经被其他M偷走并运行完成,当前M1和M2分别在运行G2和G8,
 M3和M4没有goroutine可以运行,M3和M4处于自旋状态,它们不断寻找goroutine。
 系统中最多有GOMAXPROCS个自旋的线程,多余的没事做线程会让他们休眠。

10.假定当前除了M3和M4为自旋线程,还有M5和M6为空闲的线程,
  G8创建了G9,G8进行了阻塞的系统调用,M2和P2立即解绑,P2会执行以下判断:
  如果P2本地队列有G、全局队列有G或有空闲的M,P2都会立马唤醒1个M和它绑定,
  否则P2则会加入到空闲P列表,等待M来获取可用的p。

11.G8创建了G9,假如G8进行了非阻塞系统调用。M2和P2会解绑,但M2会记住P2,
  然后G8和M2进入系统调用状态。当G8和M2退出系统调用时,会尝试获取P2,
  如果无法获取,则获取空闲的P,如果依然没有,G8会被记为可运行状态,
  并加入到全局队列,M2因为没有P的绑定而变成休眠状态

  • 上一条:
    Laravel Log Fake 2.0包用于断言/伪造记录/假日志
    下一条:
    go语言中将字符串切片转换为结构并使用反射方式读取数据
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • go语言中Pat多路复用器路由功能示例代码(0个评论)
    • go语言中HttpRouter多路复用器路由功能示例代码(0个评论)
    • go语言中回收make()占用的内存代码示例(0个评论)
    • go语言中什么时候使用make或者new(0个评论)
    • go语言实现招商银行支付签名验证功能流程步骤(0个评论)
    • 近期文章
    • GnuPG(GPG)生成用于替代SSH密钥的子密钥:签名、加密、鉴权及SSH验证(0个评论)
    • GnuPG(GPG)密钥创建的流程步骤(0个评论)
    • Laravel 9.24版本发布(0个评论)
    • windows系统phpstudy环境中安装amqp拓展流程步骤(0个评论)
    • windows10+docker desktop使用docker compose编排多容器构建dnmp环境(0个评论)
    • windows10+docker desktop运行laravel项目报错:could not find driver...(0个评论)
    • windows10+docker desktop报错:docker: Error response from daemon: user declined directory sharing(0个评论)
    • go语言中Pat多路复用器路由功能示例代码(0个评论)
    • go语言中HttpRouter多路复用器路由功能示例代码(0个评论)
    • js中使用Push.js通知库将通知推送到浏览器(0个评论)
    • 近期评论
    • nkt 在

      阿里云香港服务器搭建自用vpn:Shadowsocks使用流程步骤中评论 用了三分钟就被禁了,直接阿里云服务器22端口都禁了..
    • 熊丽 在

      安装docker + locust + boomer压测环境实现对接口的压测中评论 试试水..
    • 博主 在

      阿里云香港服务器搭建自用vpn:Shadowsocks使用流程步骤中评论 @test  也可能是国内大环境所至,也是好事,督促你该研究学习新技术..
    • test 在

      阿里云香港服务器搭建自用vpn:Shadowsocks使用流程步骤中评论 打了一次网页,然后再也打不开了。。是阿里云的缘故吗?..
    • 博主 在

      centos7中Meili Search搜索引擎安装流程步骤中评论 @鹿   执行以下命令看看你的2.27版本是否存在strin..
    • 2016-10
    • 2017-09
    • 2020-03
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-12
    • 2021-01
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-03
    • 2022-04
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客