谈谈对 GMP 的简单认识

news2025/4/11 8:57:18

犹记得最开始学习 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.coloradmin.cn/o/918502.html

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

相关文章

【0823作业】C++:实现类嵌套,以及其构造函数、析构函数和拷贝构造函数

要求&#xff1a; 设计一个Per类。类中包含私有成员&#xff1a;姓名、年龄、指针成员身高、体重&#xff1b; 再设计一个Stu类&#xff0c;类中包含私有成员&#xff1a;成绩、Per类对象 p1&#xff1b; 设计这两个类的构造函数、析构函数和拷贝构造函数。 #include <iostr…

spring之Spring Boot入门与快速启动

Spring Boot入门与快速启动 摘要:引言:词汇解释:详细介绍:什么是Spring Boot以及其特点: 什么是Spring Boot以及其特点Spring Boot 简介:Spring Boot 的特点: 注意事项:使用Spring Boot初始化项目:使用 Spring Initializr:使用 Spring Boot CLI: 注意事项:代码示例:自动配置和约…

【数据库】详解数据库架构优化思路(两主架构、主从复制、冷热分离)

文章目录 1、为什么对数据库做优化2、双主架构双主架构的工作方式如下&#xff1a;双主架构的优势包括&#xff1a;但是一般不用这种架构&#xff0c;原因是&#xff1a; 3、主从复制主从复制的工作方式如下&#xff1a;主从复制的优势包括&#xff1a;主从复制的缺点 4、冷热分…

我是怎么从0到1搭建性能门禁系统的

背景 页面的性能对于用户的体验起着至关重要的作用&#xff0c;根据Mobify 研究发现&#xff0c;首页加载时间每减少100 毫秒&#xff0c;用户留存率就会增加1.11%。所以做好页面的性能优化&#xff0c;对于网站来说是一个非常重要的步骤。 在解决问题之前需要度量问题&#x…

吴师傅教你怎样开启联想电脑管家的极速模式

如果你的笔记本出现卡顿的情况&#xff0c;可以在联想电脑管家里开启极速模式试一下&#xff0c;会有运行速度上的提升&#xff0c;具体方法如下&#xff1a; 1、双击打开桌面上的联想电脑管家&#xff1b; 2、在打开的“联想电脑管家”界面里&#xff0c;点击右上边的“实用工…

mathematica 提取Solve(NSolve)函数变量

直接上例子&#xff0c;非常直观 求解的方程是&#xff1a; 0.7 sin ⁡ ( x ) 0.7 sin ⁡ ( 2 x ) 0.6047 0.7 \sin (x)0.7 \sin (2 x)0.6047 0.7sin(x)0.7sin(2x)0.6047 提取Solve(NSolve)函数变量&#xff0c;列表提取第一个元素 列表提取第3个元素 提取第三个元素的…

timer定时器,使用timer定时器完成LED123点亮

使用timer定时器完成LED123&#xff0c;一秒亮&#xff0c;一秒灭 #include "head.h" #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/io.h> #include <l…

存在逻辑删除的表字段上建立唯一索引的巧办法 (逻辑删除与唯一索引)

存在逻辑删除的表字段上建立唯一索引的巧办法 首先&#xff0c;我们肯定是清楚地知道唯一键值逻辑删除位如果联合创建唯一索引的话&#xff0c;只能最多存在两条数据&#xff0c;无法满足不断删除新增的需求&#xff0c;所以需要一个巧妙的办法去实现有逻辑删除标志位的唯一索…

Anaconda安装pytorch-cuda

1 查看电脑对应cuda版本 【win-R】> 【cmd】> 确定 执行nvidia-smi&#xff0c;可以从图中看出&#xff0c;该电脑的CUDA Version为&#xff1a;11.6 2 官网查找对应的版本的安装语句 PyTorch官网&#xff1a;https://pytorch.org/ 2.1 可以直观的选择对应的版本 如…

LabVIEW开发聋哑人智能辅助手套

LabVIEW开发聋哑人智能辅助手套 今天的残疾人面临着许多挑战&#xff0c;最大的问题是沟通效率低下。由于这些人通常使用通信来请求基本必需品&#xff0c;因此在他们的日常生活中起着重要作用。能力不同的人通常会找到解决这个问题的方法。残疾人很难表达自己的需求&#xff…

MyBatis的场景应用(动态SQL、模糊查询及映射结果)附(Mybatis中#和$的区别)

一.Mybatis简介 MyBatis&#xff08;之前被称为iBatis&#xff09;是一种开源的持久化框架&#xff0c;它将面向关系数据库的持久层操作封装起来&#xff0c;使得开发人员可以通过简单的配置来实现对数据库的操作。MyBatis提供了灵活且强大的SQL映射功能&#xff0c;能够将数据…

关于融合项目点云pointpillars检测不显示三维检测框问题的解决

这个问题主要还是launch文件中出现了一些偏差。 launch文件的第26行 这里原先是0.6&#xff0c;在检测kitti的时候是0.6&#xff0c;由于kitti是64线激光雷达&#xff0c;我个人用的是16线激光雷达&#xff0c;所以把0.6降到了0.2.出现了三维检测框&#xff0c;问题解决

Linux 安装mysql(ARM架构)

添加mysql用户组和mysql用户 安装依赖libaio yum install -y libaio* 下载Mysql wget https://obs.cn-north-4.myhuaweicloud.com/obs-mirror-ftp4/database/mysql-5.7.27-aarch64.tar.gz安装mysql 解压Mysql tar xvf mysql-5.7.27-aarch64.tar.gz -C /usr/local/ 重命名 …

SQL注入之延时注入

文章目录 延时注入是什么&#xff1f;延时注入获取数据库版本号 延时注入是什么&#xff1f; 延时注入就是利用sleep()函数通过if语句判断所写的语句真假&#xff0c;如果为真返回我们想要的东西&#xff08;例如&#xff1a;数据库的长度&#xff0c;数据库的名字等&#xff0…

C++类练习

作业&#xff1a; 整理思维导图设计一个Per类&#xff0c;类中包含私有成员&#xff1a;姓名、年龄、指针成员身高、体重&#xff0c;再设计一个Stu类&#xff0c;类中包含私有成员&#xff1a;成绩、Per类对象 p1&#xff0c;设计这两个类的构造函数、析构函数和拷贝构造函数…

b树/b+树、时间轮、跳表、LSM-Tree

b树、b树&#xff1a;关系型数据库核心存储结构 1、为什么磁盘数据存储结构用B树、而不用红黑树 磁盘每次读取不是读一个节点、是返回一页数据。 红黑树每次遍历一个节点排除一半数据。 B树通常映射相邻的磁盘页数据。4K mysql索引一个节点隐射16k故而映射4倍&#xff0c;故…

static相关知识点详解

文章目录 一. 修饰成员变量二. 修饰成员方法三. 修饰代码块四. 修饰类 一. 修饰成员变量 static 修饰的成员变量&#xff0c;称为静态成员变量&#xff0c;该变量不属于某个具体的对象&#xff0c;是所有对象所共享的。 public class Student {private String name;private sta…

【多天线传输技术】迫零检测算法、串行干扰相消算法、排序串行干扰相消算法

clc; clear; close all; len_s100000; % 信号长度 snr0:2:20;%信噪比 len_snrlength(snr); s2_1zeros(1,4); ber_zfzeros(1,len_snr); ber_zf_siczeros(1,len_snr); ber_zf_chsiczeros(1,len_snr); for ii1:len_snrerror_zf20;for i1:len_ssnrandi([0,1],4,1); %产生随机信号mo…

35岁的前阿里员工:薪资从46K降到40K进传统企业,太香了,8.30上班,5点下班!

互联网大厂&#xff0c;对每一位程序员而言都是一个向往的地方。高薪、高压、高目标&#xff0c;每个人都为之奋斗不止。然而&#xff0c;在光鲜亮丽的外表之下&#xff0c;却隐藏着无数的焦虑与疲惫。 35岁&#xff0c;对于一个程序员来说&#xff0c;似乎是一个被现实无情提…

性能调优篇 一、概述

目录 五、测试指标 五、测试指标 1、响应时间 提交请求和返回该请求之间使用的时间、一般关注平均响应时间 在垃圾回收环境自中&#xff1a; 暂停时间&#xff1a;执行垃圾收集时&#xff0c;程序的工作线程被暂停的时间 -XX:MaxGCPauseMillis xxx2、吞吐量 单位时间内完…