Golang面试题四(GMP)

目录

1.Goroutine 定义

2.GMP 指的是什么

3.GMP模型的简介

全局队列(Global Queue)

P的本地队列

P列表

M列表

4.有关P和M的个数问题

P的数量问题

M的数量问题

P和M何时会被创建

5.调度器P的设计策略

复⽤线程

work stealing机制

hand off机制

利⽤并⾏

抢占

全局G队列

6.“go func()” 经历了什么过程

7.调度器的生命周期

M0

G0

8.Sysmon有什么作用

9.可视化GMP编程


1.Goroutine 定义

定义: Goroutine 是 Go 语言运行时系统(Runtime)管理的用户级线程(User-Level Thread),是一种可以在单个进程中并发执行的执行单元。每个 Goroutine 代表了一个独立的函数调用,可以在程序中并行地执行任务,而无需操作系统层面的线程(OS Thread)支持。

关键特性

  1. 轻量级:相较于操作系统原生线程,Goroutine 的创建、销毁和上下文切换(Context Switching)成本极低,使得开发者可以轻松地在程序中创建大量并发任务,实现高并发处理。

  2. 协程:Goroutines 之间通过协作而非抢占式调度进行切换,这意味着一个 Goroutine 只有在主动放弃 CPU 时间片(如调用系统调用、阻塞在 I/O 操作或显式调用 sync 包中的同步原语)时,运行时才会调度其他等待的 Goroutine 执行。这种非抢占式的调度有助于减少线程上下文切换的开销。

  3. Go 语言原生支持:在 Go 语言中,启动一个新的 Goroutine 非常简单,只需在函数调用前加上关键字 go,如 go someFunction()。编译器会负责将此函数封装为一个可以并发执行的 Goroutine。

  4. 调度器:Go 语言的运行时系统内置了一个高效的调度器(Scheduler),负责管理和调度所有 Goroutines 的执行。调度器可以根据系统资源(如 CPU 核心数)和 Goroutines 的运行状态动态调整其调度策略,确保并发任务高效、公平地执行。

  5. 通信代替共享内存:Go 语言鼓励通过 channels(通道)进行 Goroutines 之间的通信和同步,遵循“不要通过共享内存来通信,而应通过通信来共享内存”的原则,避免了传统并发编程中常见的数据竞争和竞态条件问题。

  6. 栈动态增长:每个 Goroutine 初始时分配一个小栈(通常为几 KB),随着函数调用深度的增加,栈空间不足时会自动增长(可达几 MB),避免了预估栈大小的难题,同时也减少了内存浪费。

综上所述,Goroutine 是 Go 语言中实现并发编程的关键特性,它是一种轻量级、协程化的执行单元,由 Go 语言运行时系统原生支持并高效调度。通过 Goroutines,开发者可以方便、高效地编写并发程序,利用多核处理器能力,同时借助 channels 和其他同步原语确保数据安全和正确性。

2.GMP 指的是什么

  • G( Goroutine): 我们所说的协程,为用户级的轻量级线程,每个Goroutine对象中的sched保存着其上下文信息。
  • M( Machine): 对内核级线程的封装,数量对应真实的 CPU 数(真正干活的对象)。
  • P( Processor): 即为 G 和 M 的调度对象,用来调度 G 和 M 之间的关联关系,其数量可通过 GOMAXPROCS()来设置,默认为核心数。

3.GMP模型的简介

在Go中,线程是运行goroutine的实体,调度器的功能是把可运行的goroutine分配到工作线程上

全局队列(Global Queue)

存放等待运行的G。

P的本地队列

  • 同全局队列类似,存放的也是等待运行的G
  • 存的数量有限,不超过256个。
  • 新建G'时,G'优先加入到P的本地队列,如果队列满了,则会把本地队列中一半的G移动到全局队列。

P列表

所有的P都在程序启动时创建,并保存在数组中,最多有GOMAXPROCS(可配置)个。

M列表

  • 当前操作系统分配到当前Go程序的内核线程数
  • 线程想运行任务就得获取P,从P的本地队列获取G,P队列为空时,M也会尝试从全局队列拿一批G放到P的本地队列,或从其他P的本地队列偷一半放到自己P的本地队列。
  • M运行G,G执行之后,M会从P获取下一个G,不断重复下去。

Goroutine调度器和OS调度器是通过M结合起来的,每个M都代表了1个内核线程,OS调度器负责把内核线程分配到CPU的核上执行。

4.有关P和M的个数问题

P的数量问题

由启动时环境变量$GOMAXPROCS或者是由runtime的方法GOMAXPROCS()决定。这意味着在程序执行的任意时刻都只有$GOMAXPROCS个goroutine在同时运行。

M的数量问题

  • go语言本身的限制:go程序启动时,会设置M的最大数量,默认10000.但是内核很难支持这么多的线程数,所以这个限制可以忽略。
  • runtime/debug中的SetMaxThreads函数,设置M的最大数量
  • 一个M阻塞了,会创建新的M。

M与P的数量没有绝对关系,一个M阻塞,P就会去创建或者切换另一个M,所以,即使P的默认数量是1,也有可能会创建很多个M出来。

P和M何时会被创建

  • P何时创建:在确定了P的最大数量n后,运行时系统会根据这个数量创建n个P。
  • M何时创建:没有足够的M来关联P并运行其中的可运行的G。比如所有的M此时都阻塞住了,而P中还有很多就绪任务,就会去寻找空闲的M,而没有空闲的,就会去创建新的M。

5.调度器P的设计策略

复⽤线程

避免频繁的创建、销毁线程,⽽是对线程的复⽤。

work stealing机制

当本线程⽆可运⾏的G时,尝试从其他线程绑定的P偷取G,⽽不是销毁线程。

hand off机制

当本线程因为G进⾏系统调⽤阻塞时,线程释放绑定的P,把P转 移给其他空闲的线程执⾏。

利⽤并⾏

  1. GOMAXPROCS设置P的数量,最多有GOMAXPROCS个线程分布在多个CPU上同时运行。
  2. GOMAXPROCS也限制了并发的程度,比如GOMAXPROCS = 核数/2,则最多利用了一半的CPU核进行并行。

抢占

  • 在coroutine中要等待一个协程主动让出CPU才执行下一个协程
  • 在Go中,一个goroutine最多占用CPU 10ms,防止其他goroutine被饿死,这就是goroutine不同于coroutine的一个地方。

全局G队列

在新的调度器中依然有全局G队列,当P的本地队列为空时,优先从全局队列获取,如果全局队列为空时则通过work stealing机制从其他P的本地队列偷取G。

6.“go func()” 经历了什么过程

1、我们通过 go func()来创建一个goroutine;

2、有两个存储G的队列,一个是局部调度器P的本地队列、一个是全局G队列。新创建的G会先保存在P的本地队列中,如果P的本地队列已经满了就会保存在全局的队列中;

3、G只能运行在M中,一个M必须持有一个P,M与P是1:1的关系。M会从P的本地队列弹出一个可执行状态的G来执行,如果P的本地队列为空,就会想其他的MP组合偷取一个可执行的G来执行;

4、一个M调度G执行的过程是一个循环机制;

5、当M执行某一个G时候如果发生了syscall或则其余阻塞操作,M会阻塞,如果当前有一些G在执行,runtime会把这个线程M从P中摘除(detach),然后再创建一个新的操作系统的线程(如果有空闲的线程可用就复用空闲线程)来服务于这个P;

6、当M系统调用结束时候,这个G会尝试获取一个空闲的P执行,并放入到这个P的本地队列。如果获取不到P,那么这个线程M变成休眠状态, 加入到空闲线程中,然后这个G会被放入全局队列中。

7.调度器的生命周期

M0

M0是启动程序后的编号为0的主线程,这个M对应的实例会在全局变量runtime.m0中,不需要在heap上分配,M0负责执行初始化操作和启动第一个G, 在之后M0就和其他的M一样了。

G0

是每次启动一个M都会第一个创建的gourtine,G0仅用于负责调度的G,G0不指向任何可执行的函数, 每个M都会有一个自己的G0。在调度或系统调用时会使用G0的栈空间, 全局变量的G0是M0的G0。 

我们来跟踪一段代码

package mainimport "fmt"func main() {fmt.Println("Hello world")
}
  1.  runtime创建最初的线程m0和goroutine g0,并把2者关联。
  2. 调度器初始化:初始化m0、栈、垃圾回收,以及创建和初始化由GOMAXPROCS个P构成的P列表。
  3. 示例代码中的main函数是main.main,runtime中也有1个main函数——runtime.main,代码经过编译后,runtime.main会调用main.main,程序启动时会为runtime.main创建goroutine,称它为main goroutine吧,然后把main goroutine加入到P的本地队列。
  4. 启动m0,m0已经绑定了P,会从P的本地队列获取G,获取到main goroutine。
  5. G拥有栈,M根据G中的栈信息和调度信息设置运行环境
  6. M运行G
  7. G退出,再次回到M获取可运行的G,这样重复下去,直到main.main退出,runtime.main执行Defer和Panic处理,或调用runtime.exit退出程序。

调度器的生命周期几乎占满了一个Go程序的一生,runtime.main的goroutine执行之前都是为调度器做准备工作,runtime.main的goroutine运行,才是调度器的真正开始,直到runtime.main结束而结束。

8.Sysmon有什么作用

sysmon是一个管理线程或者说守护线程,其是对GMP调度架构的补充和兜底。

GMP的调度完全是主动协作式的调度。主动协作式的调度性能很高,但是在某些情况下会出现单个goroutine长期占据时间片甚至一直占据时间片的情况。
比如:

  1. 某个goroutine不执行主动调度、不调用系统调用、不做函数调用,就会一直运行直到goroutine退出;
  2. 某个goroutine处于syscall状态时也无法触发主动调度,可能会造成该goroutine长时间占据时间片;

sysmon的作用就是处理类似上面情况,其主要的工作内容有:

  1. 定期查看netpoll有无就绪的任务,防止netpoll阻塞队列中的goroutine饥饿;
  2. 定期查看是否有p长时间(10ms)处于syscall状态,如有则将p的持有权释放以执行其他g;
  3. 定期查看是否有p长时间(10ms)没有调度,如有则对当前m发送信号,触发基于信号的异步抢占调度;

9.可视化GMP编程

方式1:go tool trace

方式2:Debug trace

参考:2、Golang的协程调度器原理及GMP设计思想 · 语雀

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

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

相关文章

算法 第41天 动态规划3

343 整数拆分 给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k > 2 ),并使这些整数的乘积最大化。 返回 你可以获得的最大乘积 # 动态规划 def integerBreak(n:int)->int:dp[0]*(n1)dp[2]1 #从下标为2开始 &#xff0…

本地生活服务平台都有哪些,靠谱吗?

随着本地生活服务的发展潜力和盈利方式被不断挖掘,越来越多的人开始发现其中所蕴含着的巨大商机,大家所熟悉的抖音、小红书和支付宝等平台也纷纷上线了本地生活板块,再次印证了其前景的广阔。在此背景下,普通人想要趁势入局分一杯…

定制k8s域名解析------CoreDns配置实验

定制k8s域名解析------CoreDns配置实验 1. 需求 k8s集群内通过CoreDns互相解析service名. 同时pana.cn域为外部dns解析,需要通过指定dns服务器进行解析 再有3个服务器,需要使用A记录进行解析 2. K8s外DNS服务器 查看解析文件 tail -3 /var/named/pana.cn.zone 解析内容 ww…

免费申请泛域名证书

通配符证书是一种比较特殊的SSL/TLS 证书,可用于保护多个域名(含主域名),由域名字段中的通配符 (*) 指示。这种证书主要用于具有很多子域的组织。通配符证书对主域及其所有次级子域有效。 对于免费通配符证书而言,目前…

IAR 使用笔记(IAR BIN大小为0异常解决)

烧写 由于芯片的内部SPI FLASH的0级BOOT 程序起到到开启JTAG SW 仿真功能,一旦内部SPI FLASH存储的BL0启动代码被损坏,芯片的JTAG 将不能被连接。所以对BL0的烧写需要谨慎,烧写BL0过程保证芯片不断电。 如果烧写了多备份的启动代码&#xff…

NLP基础—jieba分词

jieba分词 支持四种分词模式 精确模式 试图将句子最精确地切开,适合文本分析;全模式 把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义;搜索引擎模式 在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。paddle模式 利用Paddle…

Spring Boot 处理过滤器(filter )中抛出的异常

前言: 在改造老项目登录功能的时候,使用了过滤器对 token 进行有效性验证,验证通过继续进行业务请求,验证不通过则抛出校验异常。 过程: 技术方案拟定后,就着手开始改造,一切都很顺畅&#x…

Oracle子查询

这里写目录标题 子查询子查询的分类1.非相关子查询单行单列单行多列单列多行多行多列 2.相关子查询 子查询 定义:一个查询语句中嵌套一个或多个查询语句,里层的查询语句的结果成为了外层查询语句的一部分。 外层的查询叫主查询 里层的查询叫子查询 子查…

Linux学习:进程(5)进程控制

目录 1. 进程创建1.1 进程创建的方式与过程1.2 写时拷贝1.3 补充知识 2. 进程终止2.1 main函数返回值与进程退出码2.2 进程退出码的意义2.3 进程的执行结果与异常信号2.4 进程终止方式:exit与_exit 3. 进程等待3.1 进程创建与回收3.2 进程等待与回收的方式3.3 wait与…

鸿蒙语言TypeScript学习第18天:【泛型】

1、TypeScript 泛型 泛型(Generics)是一种编程语言特性,允许在定义函数、类、接口等时使用占位符来表示类型,而不是具体的类型。 泛型是一种在编写可重用、灵活且类型安全的代码时非常有用的功能。 使用泛型的主要目的是为了处…

树莓派安装tensorflow

树莓派安装tensorflow 使用编译好的版本自己选择版本进行编译armv71 架构 教程转载 使用编译好的版本 下载tensorflow编译好的版本 https://github.com/lhelontra/tensorflow-on-arm/tags由于python版本支持有限可能需要自己安装python 安装对应的python 自己选择版本进行编译…

【动态规划】【01背包 给定背包容量,装满背包最多有多少个物品】Leetcode 474. 一和零

【动态规划】【01背包 给定背包容量,装满背包最多有多少个物品】Leetcode 474. 一和零 解法 ---------------🎈🎈474. 一和零 题目链接🎈🎈------------------- 纯 0 - 1 背包 是求 给定背包容量 装满背包 的最大价值…

【随笔】Git 高级篇 -- 远程与本地不一致导致提交冲突 git push --rebase(三十一)

💌 所属专栏:【Git】 😀 作  者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! 💖 欢迎大…

如何爬出 Kotlin 协程死锁的坑?

作者:悬衡 一、前言 在 Java 中有一个非常经典的死锁问题, 就是明明自己已经占用了线程池, 却还继续去申请它, 自己等自己, 就死锁了, 如下图和代码: // 这段代码将死锁到天荒地老final ExecutorService executorService Executors.newSingleThreadExecutor();exe…

【C 数据结构】单链表

文章目录 【 1. 基本原理 】1.1 链表的节点1.2 头指针、头节点、首元节点 【 2. 链表的创建 】2.0 创建1个空链表(仅有头节点)2.1 创建单链表(头插入法)*2.2 创建单链表(尾插入法) 【 3. 链表插入元素 】【…

爱之旅 | 爱的守望与宽恕

爱其实是一种持续的稳定情绪输出与反馈的表现。 情绪稳定的前提是丰富。 比如生活的丰富,有物质财富、社会关系、健康、知识和经验等。 一个人的生活如果多元化,拥有丰富的兴趣爱好、健康的社交关系、持续的个人成长,在面对生活中的挑战时自…

【SpringBoot实战篇】登录认证

🍀🌸明确需求--接口文档--思路分析--开发--测试🌸🍀💕 1 明确需求 2 接口文档 登录 3 思路分析 UserServic、UserMapper在注册的时候已经实现 现在我们重点看UserController 控制器 4 开发(实现&#xff0…

做熟思维100历年真题700道,高效备考2024年思维100春季线上赛

今天是2024年4月17日,距离2024年春季思维100活动第一阶段的线上比赛4月20日还有3天。尽管主办方未提供2024年思维100活动的考试重点和大纲,但是我们仍然可以从历年的思维100真题中来分析和推测,把历年真题和背后的知识点吃透了,举…

Flink CDC:使用 Flink SQL 将多表写入一个 Kafka Topic 以及 Flink 作业数量的测试

博主历时三年精心创作的《大数据平台架构与原型实现:数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行,点击《重磅推荐:建大数据平台太难了!给我发个工程原型吧!》了解图书详情,…

JQuery(四)---【使用JQuery实现动画效果】

目录 前言 一.隐藏和显示 1.1使用方法 1.2案例演示(1) 1.3隐藏/显示效果一键切换 二.淡入淡出效果 2.1使用方法 2.2案例演示(fadeIn) 2.3案例演示(fadeOut) 2.4案例演示(fadeToggle) 2.5案例演示(fadeTo) 三.滑动 3.1使用方法 3.2案例演示(slideDown) 3.3案例演示…