文章目录

  • 引言
    • 一些相关知识
    • 演进过程
    • Goroutine
  • GPM
    • 简介
    • 结构
    • 设计策略
    • M0&G0
  • 上代码(可视化GPM调试)
  • 场景
    • 创建G
    • G执行完毕
    • G开辟过多的G
    • 唤醒正在休眠的M
    • 自旋线程的最大限制
    • G发生系统调用/阻塞
    • G从阻塞变为非阻塞

引言

一些相关知识

  • 进程占用内存:虚拟内存4GB(32bit OS)
  • 线程占用内存:大约4MB

演进过程

并发执行各种线程,切换线程会造成较大的性能损耗,多线程的同步竞争(锁、竞争资源冲突等)问题,最后还有上面说到的占用内存较大。
系统分为内核空间和用户空间,可以通过这个思想,将线程分割开来作为一个个协程co-routine,内核空间存放内核线程,用户空间存放切割后的协程,中间通过协程调度器来维持工作。
所以说,M:N的调度工作就交给了协程调度器,线程层面(即内核空间)语言控制不了,所以说语言间的区别主要在于协程调度器的设计。

Goroutine

Golang将co-routine封装,自己命名为Goroutine,内存占用个位数KB,这样就可以大量产生自己想要的协程。
Golang编写自己的调度器,使得调度更加灵活,所以GPM出现。

GPM

简介

  • G:goroutine 协程
  • P:processor 处理器
  • M:thread 内核线程

结构

  • 全局G队列:存放等待的G
  • P的本地队列:同样存放等待运行的G,存放限制数量不超过256个,优先将新创建的G存入,若已满则存入全局队列。
  • P列表:许多P组成的列表,程序启动时创建,限制数量为GOMAXPROCS(可设置)。
  • M列表:当前操作系统分配到当前OS程序的内核线程数,限制数量为10000,也可通过runtime/debug包中的SetMaxThreads设置。若有一个M阻塞,则创建一个新的M,若有空闲M,则回收或者睡眠。

设计策略

  • 复用线程:
    work stealing 机制:若某个M处于空闲状态,则可以去偷取别的M对应P队列中等待的G任务来执行。
    hand off 机制:若某个P队列中G任务阻塞,则会唤醒一个新的M,将P队列剩余G任务移入新的M,阻塞的G则等待,对应阻塞的M则回收或休眠。
  • 利用并行:通过GOMAXPROCS限定P的个数,空出一些P给别的程序使用。
  • 抢占:若GA占用CPU时,GB来请求使用,则GA最多使用10ms后释放CPU,再次排队使用。
  • 全局G队列:对于work stealing的补充,空闲的M先去全局G队列中拿取,若全局G队列为空,则再执行work stealing机制去别的M中偷取。(注:全局队列会有锁机制,需要加锁解锁进行偷取)。

M0&G0

  • M0: 程序启动时OS分配给程序的编号0的主线程,在全局变量runtime.m0中,不需要在heap上分配,负责执行初始化和启动第一个G,启动第一个G之后,M0就和其他的M一样了。
  • G0:每个M启动时,都会先创建一个goroutine,它就是G0。G0负责调度M中的各种G任务,从不指向任何可执行的函数,在调度或者系统调用时,M都会先切换到G0,然后通过G0来调度其 他G任务。
    M0的G0会放在全局空间。

上代码(可视化GPM调试)

func main() {// 创建trace文件traceFile, err := os.Create("trace.out")if err != nil {log.Fatalln(err.Error())return}defer traceFile.Close()// 启动traceerr = trace.Start(traceFile)if err != nil {log.Fatalln(err.Error())return}// 逻辑代码fmt.Println("Hello GPM~")// 停止tracetrace.Stop()
}

编译运行后会得到一个trace.out文件,然后通过go tool trace工具可以分析trace文件,返回一个url通过浏览器可视化,如下图。

场景

创建G

局部性原则:由一个G1创建一个新的G3时,优先将新的G3放入和G1相同的本地队列中。

G执行完毕

当本地队列一个G执行完毕后,先切换为G0,然后通过G0先去调用本地队列中的G任务。

G开辟过多的G

如果一个G申请创建过多的G,超过了本地队列的最大值,根据创建G的场景,会先依次将本地队列存满,这时本地队列已满,但还有新创建G的需求,比如G8,那么就会将本地队列一分为二,将前一部分的移入全局队列,后一部分往前移 ,并将G8也移入全局队列,此时已有空闲空间,若还有新的G申请创建,则正常放入本地队列即可,循环反复。

唤醒正在休眠的M

每次新创建一个G时,运行的G会尝试唤醒其他空闲的P和M组合去执行。假定唤醒了M2,M2绑定了P2,此时M2会运行自身的G0,但P2队列本身没有G任务,那么M2此时就为自旋线程(没有G任务,但是在运行状态的线程,会不断地寻找G,先从全局G队列中拿取,全局队列为空时,再去别的进程偷取,并且为批量偷取,一分为二,将后半部分全部偷取,总共自旋线程获取数量n符合如下公式)。
n = min(len(GQ) / GOMAXPROCS + 1, len(GQ / 2))

自旋线程的最大限制

  • 自旋线程 + 执行线程 <= GOMAXPROCS
  • 多余没事儿做的线程会进行休眠处理,而不是自旋。

G发生系统调用/阻塞

假设当前M1和M2在工作,M3和M4为自旋线程,M5和M6为休眠队列中的休眠线程,即最大线程数为4。当前M2对应的P2中的G1发生系统调用/阻塞且队列中还有G2任务,则M2和P2立即解绑,P2会执行以下判断:如果存在P2本地队列中还有G任务或全局G队列中有G任务或休眠线程队列中有空闲的M,则P2都会立马唤醒一个M与其绑定,否则P2则会加入到空闲P列表,等待M来获取可用的P。上例中,P2本地队列中还有G2任务,则可以与空闲线程队列中的M5进行绑定,继续后续工作。

G从阻塞变为非阻塞

接上例,若G1从阻塞变为非阻塞,此时对应的M2会优先尝试获取原先的P2,若P2已被绑定在别的M,则从空闲P队列中获取,若空闲队列中为空,则M2获取P失败,直接将M2放入休眠线程队列,G变为可运行状态,放入全局G队列中。
————————————————
版权声明:本文为CSDN博主「Pekue」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Pekue/article/details/116237421

Go语言的GPM模型相关推荐

  1. Go语言并发原理 | GPM模型

    并发和并行 Go语言并发并行概念 协程 GPM模型 Go语言并发并行概念 首先对于go语言来说分为并发和并行 1.并发:并发是指在很短的时间内完成多个任务,只是宏观上的并发,其实是cpu的切换,对于我 ...

  2. Golang深入理解GPM模型

    1.goroutine简介 多进程.多线程已经提高了系统的并发能力,在高并发场景下,如果一个线程阻塞cpu,那就需要切换其他线程中去执行,为每个任务都创建一个线程成本比较高,因此又衍生出协程.我们知道 ...

  3. 从操作系统漫谈GOLang GPM模型

    前言 本文从操作系统谈起,简单介绍操作系统基本知识,引出进程.线程调度的基本原理和基本模型,然后从0到1设计Golang调度器,通过方案的逐步演进升级,可以了解到GPM模型设计理念. 阅读本文会了解到 ...

  4. Go语言的CSP模型

    前言 go语言的最大两个亮点,一个是goroutine,一个就是chan了.二者合体的典型应用CSP,基本就是大家认可的并行开发神器,简化了并行程序的开发难度,我们来看一下CSP. 一.CSP是什么 ...

  5. R语言构建xgboost模型:控制训练信息输出级别verbose参数

    R语言构建xgboost模型:控制训练信息输出级别verbose参数 目录 R语言构建xgboost模型:控制训练信息输出级别verbose参数

  6. R语言构建回归模型并进行模型诊断(线性关系不满足时)、进行变量变换(Transforming variables)、使用car包中的boxTidwell函数对预测变量进行Box–Tidwell变换

    R语言构建回归模型并进行模型诊断(线性关系不满足时).进行变量变换(Transforming variables).使用car包中的boxTidwell函数对预测变量进行Box–Tidwell变换 目 ...

  7. R语言构建xgboost模型:基于稀疏数据(dgCMatrix which is a sparse matrix)、稠密数据(dense matrix)、xgb.DMatrix数据聚合

    R语言构建xgboost模型:基于稀疏数据(dgCMatrix which is a sparse matrix).稠密数据(dense matrix) 目录

  8. R语言构建xgboost模型:使用xgb.DMatrix保存、加载数据集、使用getinfo函数抽取xgb.DMatrix结构中的数据

    R语言构建xgboost模型:使用xgb.DMatrix保存.加载数据集.使用getinfo函数抽取xgb.DMatrix结构中的数据 目录

  9. R语言使用线性回归模型来预测(predict)单个样本的目标值(响应值、response)实战

    R语言使用线性回归模型来预测(predict)单个样本的目标值(响应值.response)实战 目录

最新文章

  1. 利用FRIDA攻击Android应用程序(三)
  2. rqnoj 496 [IOI1999]花店橱窗布置 (简单dp)
  3. SQL Server 行列转换(1)
  4. Java网络编程二:Socket详解
  5. 机器学习基础(十二)—— 数学基本理论拾遗
  6. linux 脚本启动oracle,linux自动启动 oracle脚本
  7. golang 最小堆排序实现
  8. paip.dom4j中 selectSingleNode 或selectNodes获取不到节点的原因总结
  9. 初装Windows11无法打开Windows安全中心主界面
  10. css3ps插件,Photoshop图层转CSS3代码之神器-CSS3Ps插件
  11. 思岚激光雷达+cartographer建图
  12. 政策利好市场需求双加持,粉笔科技双轨并进强势突围
  13. C# 高并发场景下 共享内存 Actor并发模型到底哪个快?
  14. cmd怎么查看当前静态路由_计算机cmd命令之route,查看路由表,或配置一个更有效的路由...
  15. 一文带你认清云服务器和轻量应用服务器的区别
  16. 英国和中国的时差是多
  17. Dzzoffice 部署
  18. Axure下载安装汉化
  19. 西南石油大学计算机科学学院李欣,南充西南石油大学财经学院学霸寝室 6女生一年70张奖状...
  20. 如何写软件测试人员的周报(或日报)

热门文章

  1. Android,通讯录导入,contacts,联系人
  2. nasm纠正性训练指南pdf_打开部队军事体能训练科学化的钥匙军人身体运动功能评估(EMPF)...
  3. 鸿蒙王者荣耀想要转区吗,王者荣耀账号怎么转区 角色迁移转区教程
  4. 【渝粤题库】陕西师范大学292331 证券投资学Ⅰ作业(高起专)
  5. 用canvas画阴阳鱼
  6. el-table 固定表头,固定列,动态高度,最简单的办法
  7. 用Python做个小网站(MVC架构)
  8. 我凭什么为美国人买单?
  9. php mine类型大全
  10. 计算机学习常用网站总结