谈谈对 GMP 的简单认识

犹记得最开始学习 golang 的时候,大佬们分享 GMP 模型的时候,总感觉云里雾里,听了半天,并没有一个很清晰的概念,不知 xmd 是否会有这样的体会

虽然 golang 入门很简单,但是对于理解 golang 的设计思想和原理,还是需要一定时间的积累和沉淀,更多的应该是思想上的沉淀

希望这篇文章能够对你了解 golang 的 GMP 模型有一点帮助

文章分别从一下三个方面来谈谈我对 GMP 模型认识

  • golang 中调度器的变化及其作用
  • 有了进程,线程,为什么会出现协程
  • GMP 模型中的 G,M,P 分别都做着什么样的事情

golang 中的调度器的变化及其作用

调度器,scheduler

怎么理解呢?调度器就像是一个管理者,负责安排事项,负责调度不同人在指定时间在某个岗位上完成自己的价值交付

正如 linux 调度器一样,将就绪的进程调度成执行状态,或者将执行状态的进程,打断,变成阻塞状态,再变成就绪状态

比如说一个经典的单进程 和 多进程/多线程的操作系统,

我们可以看到在单进程系统中,只需要无脑的将进程串行排列好, CPU 会串行去执行任务,如果遇到进程1 阻塞的情况,其他进程也没有办法被 cpu 执行,那么进程2 ,进程3 ,进程4 就都要等待前面的进程完成执行完毕,才能到自己执行

可以看出单进程对于 CPU 的使用过于任性,浪费 CPU 的资源,演进到多进程/多线程操作系统的时候,就出现了调度器

上图中我们可以对比看到,在多进程/多线程的操作系统中,cpu 的时间片被分割的更加的小,对于 cpu 资源的利用率是大大的增加了,因为 cpu 可以在进程1阻塞的时候,切换去执行进程2

例如,当进程1 执行过程中,发生了阻塞,那么调度器就会就会将 cpu 切换到进程2 中进行执行,同理,进程2 阻塞的时候, cpu 就会被切换到进程 3 进行执行,当然,这就看是哪个进程先抢到 cpu 资源了

可以看到,调度器在这里的作用就是最大限度的利用上 CPU 的资源,管理进程在 CPU 上按照一定的的顺序执行任务,就好比一个优秀的管家可以合理安排好不同的员工在指定的时间上专注的处理某项事务

那么 golang 的调度器是不是也是和 linux 中的调度器有着想通之处呢?

来看看调度器在 golang 中的具体作用是干啥的

在 golang 中,调度器的实现简单来看实际上是由协程和线程按照一定的逻辑来组合起来的,其实也是扮演着一个协调和调度的作用,调度的对象是协程和线程,协程是需要被调度到线程中来运行的,这个动作就是调度器干的

  • 通常用 G 来表示协程
  • 用 M 来表示线程

正如是这样来实现调度器的:

我们可以看到,当每一个 M 想要从全局队列中取 G 出来执行的时候,是需要访问全局队列的锁,这是一把互斥锁,同一时间只能有 1 个 M 在访问

Golang 的调度器此处当然也能让多个 M 处理不同的 G,达到多进程/多线程 并发处理事项的目的

可我们知道,加锁,是会影响到我们系统的整体性能的,毕竟那么多 M 都在竞争这一把锁,势必同一时间没有抢到的锁的 M 就要等待了

为什么调度器会被淘汰掉?

看到上图,细心的我们可以发现,

所以,这个调度器缺点也是比较明显的:

第一,多个 M 都要并发的去获取全局队列中的 G ,会造成锁竞争 , 此处操作全局队列的情况有如下几种情况

  • 创建 G
  • 调度 G
  • 销毁 G

第二, 线程 M 在执行 G 的过程中,如果 G 阻塞了,那么 M 也就阻塞了,这个时候,CPU 就需要切换到其他的 M 上执行 G,这个切换的过程也是对于 CPU 资源的浪费

正如人的大脑,同时处理多个事项,当 事项 1 未处理完毕,就去处理事项 2,那么需要重新理清思绪去执行,若这个时候又需要切换到 事项 1 的时候,就需要花更多的时间来回顾之前事项 1 处理的上下文了, 所以在工作中,没有特别必要,不要打扰别人

第三, 我们知道在 golang 中,一个 G 也是可以再创建 G 的,就叫 G1 吧,那么创建的这个 G1 就需要交给其他的 M 来进行执行了,因为当前的 M 正在执行 G,自己忙不过来,因此需要将 G1 交给 M1 执行

这也是一个很明显的问题

正如上述例子一样, 2 个人排查相关问题,但是信息和认知不一致,很明显是小猪可能是比较难排查出问题来的

就像 M 执行 G 新创建了 G1,但是是交给 M1 来执行的, M1 并不知道 G1 和 G 的关系,M1 执行起来就可能会出现问题,例如需要 G 的一些上下文,但是 M1 并不知情

因此,随着时间的推移,对于性能的要求是越来越高,当然是要想办法换一个与时俱进,需要符合时代潮流的管家了,自然就出现了一个新的调度器替代了原有的调度器

出现新的调度器,自然是解决了旧的调度器的缺点,并且还带来了一些新的属性和价值,具体的新调度器策略我们可以在下文中进行展示

为什么会出现协程

在来看另外一个问题,为什么会出现协程,自然是因为使用进程和线程不能够满足我们的某些需求了,此处的需求是指对于性能的要求,是对 CPU 利用效率的需求

上图中我们有说到,对于多进程/多线程并发的时候,我们有提高 CPU 的利用率,尽可能的利用好 CPU 的时间片,但是对于 CPU 从 进程/线程1 切换到去执行进程/线程 2 的过程中,是会产生消耗的,但是这个消耗很难避免

那么我们知道 CPU 其实执行的是线程,那么如果 1 个线程里面还可以分成多个程序进行并发岂不是可以大大的提高我们当前线程的使用效率?

我们从 C/C++ 中知道,咱们的一个32位系统的机器,进程实际上是开辟了一个 4G 的虚拟空间,具体 4G 虚拟空间都包含了什么,我们可以简单的看看这个图,本次不在此细聊

那线程大概也是需要 4M 的空间,那么我们在一定程度上大量的开辟进程或者线程,必然会带来系统中 内存占用高,调度 CPU 切换的消耗高 的问题

后来,我们知道线程实际上是分为内核态和用户态的

当然,用户态的线程是依赖于内核态线程的,用户态中需要执行的内容,是需要放在内核态线程上进行执行的,另外, CPU 只知道有内核态线程的存在,意味着,CPU 只认内核态线程

此处说的内核态,和用户态,其实就是对应到我们说的 M 和 G

  • 内核态线程 – 线程
  • 用户态线程 – 协程

协程不会陷入到内核态中,因此在 M 不变的情况下,切换 G 就是非常轻快的了,协程简要有如下特点:

  • 占用内存空间小,只占用 几 kb ,比起进程,线程来说,真的是很小了
  • 调度灵活,他是处于用户态进行调度的

根据协程和线程处于的用户态和内核态,我们可以看到调度的机制是不一样的,

内核态中的线程,实际上是抢占式的,是又 CPU 调度的

用户态中的线程,即协程,是由用户态调度的,此处的用户态调度,就可以理解是一个队列了,只能一个一个的去执行了,一个协程执行完毕之后,让出 CPU ,才会执行下一个协程

好比我们代码中,多个协程,其中 1 个协程 panic 了,如何不捕获的话,是不是整个程序都崩溃了

对于 CPU 只认内核态线程这一点,咱理解起来就想到与一个公司的老板,只认每个产品线上的总监一样,你这个主管后面不管有多人少人在干活,老板只认为是你这个主管在干活

当然一个公司也不仅仅只有 1 个老板,就像计算机系统里面也会有多个 CPU ,但是道理是一样的

如何理解 GMP 模型

GMP 分别表示 协程 goroutine,线程 thread,处理器 processor

他们三组成了新的调度器,他们三者的关系这样的

咱们从图中可以知道有全局队列,本地队列

全局队列还是和之前一样,从里面里面 G 的创建,调度,销毁 都是需要访问互斥锁的

P 的本地队列 自己维护了一个 G 列表,最长是 256 个那 P 的个数一般是 GOMAXPROCS 个,如果本地的队列满了,那么 P 会将队列中的一半给到全局队列中

从本地队列中取 G 是不需要加锁的,直接取即可,且如果队列中的 G 又创建了 G1 ,那么这个 G1 也是会被优先加入到当前本地队列的

此处我们可以看到 M 是和 P 进行对应了,当 M 需要运行 G 的时候,就需要先找到一个合适的 P ,从 P 中获取 G, 如果 P 为空,那么 M 就会从全局队列中获取一批 G 放到 P 的队列中

或者是 M 也会从其他的 P 的队列中偷一半的 G 到当前 P 的队列中

从这里我们可以知道,新的调度器,已经解决了旧的调度器的一部分问题了(不需要每次都去找全局队列)

那么 P 是啥时候创建,创建多少个?M 又是啥时候创建的,创建多少个呢? P 和 M 在数量上有必然联系吗?

P 是在程序启动的时候,读取环境变量中 GOMAXPROC ,来创建具体 P 的个数

M 的创建实际上是,若 P 中有很多任务 G ,如果有空闲的 M,那么 P 就会找任意空闲 M 来进行处理,如果没有空闲的 M ,那么调度器就会去创建 M 来执行 P 中的 任务

那么新的调度器还有哪些优势呢?

第一

提高了线程的复用率,如果当前线程执行完当前 P 的任务之后,当前的 M 会尝试去偷其他 P 里面的 G 来进行执行,这样我们就尽可能的避免了线程的创建,销毁带来的开销

第二

如果当前的 M 在执行 G 的时候,出现了阻塞,那么 M 也会很懂事的让出当前的 P,让其他 M 来执行 P 中的任务当前的 M 就继续处理当前的 G

第三

上述我们说到当前 M 会去从其他 P 的队列中偷 G,这个是在当前 M 对应的 P 中没有 G 的时候,优先去做的事情, 如果其他的 P 也没有 G 的时候,当前 M 才会去全局队列中拿

从这里就可以开始,新的调度器,已经是在大大的弱化了全局队列的作用

本次先聊到这里,相信你对 GMP 的基本理论也有一些了解了吧

感谢阅读,欢迎交流,点个赞,关注一波 再走吧

可以进入地址进行体验和学习:https://xxetb.xet.tech/s/3lucCI

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

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

相关文章

threejs特殊几何体(一:文字几何体对象)

threejs中文字几何体通过newTextGeometry&#xff08;&#xff09;生成&#xff0c;它被单独作为一个类存在于threejs中const txtGeo new TextGeometry("threejs", { ...opts, font: font }); 我们先看效果&#xff1a; <template><div></div> &…

Jumpserver堡垒机管理(安装和相关操作)-------从小白到大神之路之学习运维第89天

第四阶段 时 间&#xff1a;2023年8月28日 参加人&#xff1a;全班人员 内 容&#xff1a; Jumpserver堡垒机管理 目录 一、堡垒机简介 &#xff08;一&#xff09;运维常见背黑锅场景 &#xff08;二&#xff09;背黑锅的主要原因 &#xff08;三&#xff09;解决背黑…

五种 CSS 位置类型以实现更好的布局

在 Web 开发中&#xff0c;CSS&#xff08;层叠样式表&#xff09;用于设置网站样式的设置。为了控制网页上元素的布局&#xff0c;使用CSS的position属性。因此&#xff0c;在今天这篇文章中&#xff0c;我们将了解 CSS 位置及其类型。 CSS 位置属性用于控制网页上元素的位置…

如何基于自己训练的Yolov5权重,结合DeepSort实现目标跟踪

网上有很多相关不错的操作demo&#xff0c;但自己在训练过程仍然遇到不少疑惑。因此&#xff0c;我这总结一下操作过程中所解决的问题。 1、deepsort的训练集是否必须基于逐帧视频&#xff1f; 我经过尝试&#xff0c;发现非连续性的图像仍可以作为训练集。一个实例&#xff0…

二叉树、红黑树、B树、B+树

二叉树 一棵二叉树是结点的一个有限集合&#xff0c;该集合或者为空&#xff0c;或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。 二叉树的特点&#xff1a; 每个结点最多有两棵子树&#xff0c;即二叉树不存在度大于2的结点。二叉树的子树有左右之分&#xf…

HPC是如何助力AI推理加速的?

高性能计算&#xff08;High-Performance Computing&#xff0c;HPC&#xff09;通过提供强大的计算能力、存储资源和网络互联&#xff0c;可以显著地辅助人工智能&#xff08;AI&#xff09;应用更快地进行训练和推断。那么&#xff0c;HPC是如何助力AI推理加速的&#xff1f;…

PostgerSql

建表修改字段等语句 1.建表 create table student ( id int, name varchar(30), birthday date, score numeric(5,2) ) 2.修改表名称 alter table student rename to student1; 3.修改表中列的类型 alter table student1 alter column name type varchar(40); 4.删除表的…

java八股文面试[多线程]——什么是线程安全

对线程安全的理解 总结&#xff1a;一个进程内的多个线程同时访问堆内存。 知识来源&#xff1a; 【并发与线程】对线程安全的理解_哔哩哔哩_bilibili

9.4 集成功率放大电路

OTL、OCL 和 BTL 电路均有各种不同输出功率和不同电压增益的集成电路。应当注意&#xff0c;在使用 OTL 电路时&#xff0c;需外接输出电容。为了改善频率特性&#xff0c;减小非线性失真&#xff0c;很多电路内部还引入深度负反馈。这里以低频功放为例。 一、集成功率放大电路…

创邻科技张晨:图数据库,激活数据要素的新基建

“数据经济时代&#xff0c;数据要素产业链的各细分领域均蕴含机遇&#xff0c;图技术作为网络协同和数据智能的底层发动机&#xff0c;将深度掘金数字中国价值潜能”。 8月22日&#xff0c;在2023中国&#xff08;南京&#xff09;国际软件产品和信息服务交易博览会的信息技术…

Selenium 遇见伪元素该如何处理?

问题发生 在很多前端页面中&#xff0c;大家会见到很多&#xff1a;:before、::after 元素&#xff0c;比如【百度流量研究院】&#xff1a; 比如【百度疫情大数据平台】&#xff1a; 以【百度疫情大数据平台】为例&#xff0c;“累计确诊”文本并没有显示在 HTML 源代码中&am…

【项目实战典型案例】05.前后端分离的好处(发送调查问卷)

目录 一、背景二、思路三、过程1、主要的业务逻辑2、解决问题的思路 四、总结五、面向对象的好处 一、背景 以下流程图是给用户发送调查问的整体流程&#xff0c;将不必要的业务逻辑放到前端进行处理。这样导致逻辑混乱难以维护。前后端分离的其中一个目的是将功能的样式放在了…

AODV代码实现详解——原理与源码分析(一)

首先来几个标准参考&#xff1a; RFC 3561 RFC 3561 中文翻译 一个博客 挺好的另一个博客 事件&#xff1f; 字段长度&#xff1f; 事件驱动 各种定时器 状态转移图&#xff1f; AODV协议 基本概念 AODV&#xff08;Ad hoc On-Demand Distance Vector&#xff09;是一种基于…

门禁系统忘记登入密码,现在更换电脑如何迁移旧电脑门禁系统的数据

环境&#xff1a; ivms-4200 v3.10.0.6_c 问题描述&#xff1a; 门禁系统忘记登入密码,现在更换电脑如何迁移旧电脑门禁系统的数据&#xff0c;旧电脑记住密码&#xff0c;忘了密码和密保了 解决方案&#xff1a; 1.前往海康官网下载4200客户端&#xff0c;在新电脑上安装 …

使用锐捷RG-EG210G-E路由器实现两个IP地址冲突的局域网互通

需求背景&#xff1a; 之前写过一篇博文使用路由器实现三个不同网段局域网内的计算机相互访问&#xff0c;链接如下 https://blog.csdn.net/agang1986/article/details/131862160 当前的需求又发生了变更&#xff0c;有两个独立的局域网&#xff0c;内部的计算机个数和配置的IP…

HTTPS 握手过程

HTTPS 握手过程 HTTP 通信的缺点 通信使用明文&#xff0c;内容可能被窃听(重要密码泄露)不验证通信方身份&#xff0c;有可能遭遇伪装(跨站点请求伪造)无法证明报文的完整性&#xff0c;有可能已遭篡改(运营商劫持) HTTPS 握手过程 客户端发起 HTTPS 请求 用户在浏览器里…

go vet中的那些检测项

go vet 是 Go 语言自带的一个工具&#xff0c;用于分析 Go 代码中的常见错误和潜在问题。它可以检查代码中可能存在的各种问题&#xff0c;例如&#xff1a; 未使用的变量、函数或包 可疑的函数调用 错误的函数签名 程序中的竞态条件 错误的类型转换等 本文意图指令当前go vet所…

如何编译打包OpenSSH 9.4并实现批量升级

1 介绍 openssh 9.4版本已于8月10号发布&#xff0c;安全团队又催着要赶紧升级环境里的ssh版本&#xff0c;本文主要介绍Centos5、Centos6、Centos7下openssh 9.4源码编译rpm包以及批量升级服务器openssh版本的方法。关注公众号后台回复ssh可获取本文相关源码文件。 https://w…

QT 消息对话框按钮显示

前言 搞QT嘛&#xff0c;大多数都是军工。都要国产化&#xff0c;而且消息对话框的按钮的英文也不是很得劲&#xff0c;所以需要汉化。使用静态函数的按钮就是显示英文&#xff0c;汉化的代码如下。 void Widget::on_pushButton_clicked() {QMessageBox box(QMessageBox::Inf…