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

谈谈对 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

相关文章:

谈谈对 GMP 的简单认识

犹记得最开始学习 golang 的时候,大佬们分享 GMP 模型的时候,总感觉云里雾里,听了半天,并没有一个很清晰的概念,不知 xmd 是否会有这样的体会 虽然 golang 入门很简单,但是对于理解 golang 的设计思想和原…...

Java正则表达式系列--从字符串中提取字符串或数字

原文网址:Java正则表达式系列--从字符串中提取字符串或数字_IT利刃出鞘的博客-CSDN博客 简介 本文用示例介绍Java如何使用正则表达式从字符串中提取想要的内容(字符串或者数字等)。 例1:提取一次不同内容 需求 从字符串中找到…...

机器学习实战之模型的解释性:Scikit-Learn的SHAP和LIME库

概要 机器学习模型的“黑箱”困境 机器学习模型的崛起让我们惊叹不已!不论是预测房价、识别图片中的猫狗,还是推荐给你喜欢的音乐,这些模型都表现得非常出色。但是,有没有想过,这些模型到底是如何做出这些决策的呢&a…...

Go 语言进阶与依赖管理 | 青训营

Powered by:NEFU AB-IN 文章目录 Go 语言进阶与依赖管理 | 青训营 语言进阶依赖管理测试 Go 语言进阶与依赖管理 | 青训营 GO语言工程实践课后作业:实现思路、代码以及路径记录 语言进阶 Go可以充分发挥多核优势,高效运行 Goroutine是Go语言中的协程…...

hyperf 十三 视图

教程:Hyperf composer地址:hyperf/view - Packagist 本次测试使用twig twig composedr地址:twig/twig - Packagist twig 文档地址:Home - Twig - The flexible, fast, and secure PHP template engine 一、安装 composer re…...

请你说说前端图形图像的框架

前端图形图像方面有许多强大的框架和库,使得开发者能够更容易地创建丰富的视觉效果和复杂的图形应用。下面列举了一些主要的框架和库: 1. Three.js Three.js 是一款运行在浏览器中的 3D 引擎,你可以用它创建各种三维场景,包括了…...

C++数据结构学习——栈

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、栈二、C语言实现1.声明代码2.实现增删查改代码3.测试代码 总结 前言 栈(Stack)是计算机科学中一种常见的数据结构,它是…...

【C++笔记】C++之类与对象(下)

【C笔记】C之类与对象(下) 1、再看构造函数1.1、构造函数的初始化列表1.2、C支持单参数的构造函数的隐式类型转换1.3、匿名对象 2、Static成员2.1、为什么要有静态成员变量?2.2、一个类的静态成员变量属于这个类的所有对象2.3、静态成员函数 3、友元3.1、…...

管理类联考——英语——实战篇——大作文——图表——动态图表——整体效果

动态图表模板 What is clearly presented in the above 图表类型 is that dramatic changes have taken place in 主题词1 from 年份1 to 年份2.During the period, there was a marked jump from 数字1 to 数字2 in 事物1,while that of 事物2 declined significantly from 数…...

threejs纹理加载三(视频加载)

threejs中除了能把图片作为纹理进行几何体贴图以外,还可以把视频作为纹理进行贴图设置。纹理的类型有很多,我们可以用不同的加载器来加载,而对于视频作为纹理,我们需要用到今天的主角:VideoTexture。我们先看效果&…...

VUE笔记(三)vue的语法

一、计算属性 1、计算属性的概念 计算属性是依赖于源数据(data或者属性中的数据),在元数据的基础上进行逻辑运算后得到的新的数据,计算属性要依赖于源数据,源数据数据变化计算属性也会变化 2、计算属性的语法 在vue2中使用computed这个选…...

探讨uniapp的路由与页面生命周期问题

1 首先我们引入页面路由 2 页面生命周期函数 onLoad() {console.log(页面加载)},onShow() {console.log(页面显示)},onReady(){console.log(页面初次显示)},onHide() {console.log(页面隐藏)},onUnload() {console.log(页面卸载)},onBackPress(){console.log(页面返回)}3 页面…...

咸鱼之王俱乐部网站开发

我的俱乐部 最新兑换码 *注意区分大小写,中间不能有空格! APP666 HAPPY666 QQ888 QQXY888 vip666 VIP666 XY888 app666 bdvip666 douyin666 douyin777 douyin888 happy666 huhushengwei888 taptap666 周活动 宝箱周 宝箱说明 1.木质宝箱开启1个…...

Electron+Vue3+TS 打包exe客户端

Electron Vue3 TS 实战 - 掘金 如果报错loaderContext.getOptions is not a function ts-loader版本不一致导致的问题。 解决方案:npm install ts-loader8.0.0 --save...

vue3范围选择组件封装

个人项目地址: SubTopH前端开发个人站 (自己开发的前端功能和UI组件,一些有趣的小功能,感兴趣的伙伴可以访问,欢迎提出更好的想法,私信沟通,网站属于静态页面) SubTopH前端开发个人站…...

能被整除的数(容斥原理)

思路: (1)需求:求对于1~n中至少能被p1~pm至少1个整除的数的个数,由于都是质数,彼此互质,不需要进行质因子分解,根据容斥原理, res n/p1 n/p2 ... n/pm - n /(p1p2) -…...

Modbus转Profinet网关与流量变送器兼容转ModbusTCP协议博图配置

首先,我们需要明确电磁流量计的通信协议是Modbus,而西门子1200PLC的通信协议是Profinet。这两种协议在功能和特性上存在一定的差异,因此需要使用兴达易控Modbus转Profinet网关设备进行转换。兴达易控的XD-MDPN100是Profinet转ModbusTCP的网关…...

HLS实现CORDIC算法计算正余弦并上板验证

硬件:ZYNQ7010 软件:MATLAB 2019b、Vivado 2017.4、HLS 2017.4、System Generator 2017.4 1、CORDIC算法计算正余弦 CORDIC算法详细分析网上有很多资料,它的原理是用一系列旋转去逼近目标角度,这一系列旋转的角度为 θ a r c t…...

高阶数据结构并查集

目录: 并查集的概念代码实现 LeetCode例题 并查集的概念 将n个不同的元素划分成一些不相交的集合。开始时,每个元素自成一个单元元素集合,然后按一定的规律将归于同一组元素的集合合并。在此过程中反复遇到查询某一个元素属于那个集合的运算…...

WSL2连接不了外网怎么办?

某天忽然WLAN变成地球图标,上不了Internet,搞了半天网络适配器,仍然不行。回忆之前做过的操作,曾经运行过ZoogVPN,试着启动并连接,然后退出,WLAN神奇地恢复了连接,可以上Internet了。…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...

git: early EOF

macOS报错&#xff1a; Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...