go学习-GMP模型

2023-09-15 13:01:32

GMP 好理解还是 GPM 好理解?

在这里插入图片描述
按照上述图,从上往下,GPM更适合理解
GMP 模型: Go 语言运行时系统中的 Goroutine、用于管理 Goroutine 调度的 Go Scheduler(P)、机器可用的逻辑处理器数量(M)。

理解GPM

G

每个 Goroutine 是一个轻量级“线程”,称之为“协程”,可由 Go 运行时系统并发执行

G与P的关系

Goroutine 通过 Go Scheduler 调度运行
Go Scheduler 负责在适当时间调度 Goroutine 的执行
并在必要时将 Goroutine 阻塞和调度到其他 Goroutine 执行上下文

P

P 是中介者,连接 Goroutine 和 M,由于 Go Scheduler 在用户 CPU 时间片内并行执行 Goroutine,因此也必须用 P 进行有效的调度。
Go 运行时系统可以自动调整 P 的数量,以确保它们与当前运行的 Goroutine 的数量大致相同,并且可以在需要更多/更少 P 时及时适应。

P 叫 go运行时的逻辑并发单元,每个OS线程需要获取一个P才能执行
每个P中都包含运行一个线程所需的全部资源(比如线程栈),包括存放G的就绪队列,以及分配堆内存所需的地址空间等
这些在运行main.main前就在runtime.main中(程序启动时)创建好,所以并发执行时就可以避免冲突

P的本地队列上限默认最多 GOMAXPROCS = 256 个,如果想要更改队列长度限制,使用 runtime.GOMAXPROCS(n) 调整

M

M 是处理器,绑定一个具体的OS线程(内核线程),Goroutine 运行的上下文在这里被调度并执行,所以也是 Goroutine 执行的上下文
管理 Goroutine 的运行状态、切换执行上下文
Go 运行时系统会根据机器硬件和条件动态地分配这些处理器,以确保 Goroutine 能够高效地并发运行

一个 M 最多有一个空闲的 P,有一个M阻塞,会创建或者唤醒一个新的M;若有空闲,则会回收或睡眠此M。
全局队列作为一个候选队列,可以同时为不同的 P 提供 Goroutine,从而使 Goroutine 尽快找到执行线程(M)
如果所有的 P 的本地队列都已满,并且全局队列也已满,Go 运行时系统将通过动态增加 M 的数量来解决压力
以尽快地使 Goroutine 开始运行,但OS是否愿意加M,则需要根据系统负载来定。

在默认情况下,全局队列的大小是默认 P 的数量的系数,最小值为 4,最大值为 32768

OS分配到当前Go程序的内核线程数

使用 runtime.NumCPU() 查询M数量,返回当前系统可用的逻辑处理器数量

返回值可能会因为不同的操作系统和系统配置而有所不同
有些操作系统可能会在系统负载高峰期动态增加或减少 M 的数量,以适应不同的负载需求
只会返回运行时系统认为当前可用的逻辑处理器数量,而不是一成不变的固定值

当所有 P 的本地队列都已满,且全局队列也满时,有新的Goroutine就绪

  • Go 运行时系统会请求操作系统分配更多线程,以进一步提高系统并发处理的能力,启动一个新线程,即新的 M (machine)
  • 新的 M 将从全局队列中拿走一个等待 Goroutine 并开始运行它
  • 新的 M 将在全局队列和所有本地队列之间轮询,直到所有队列都变得空闲或有 Goroutine 准备好运行,这将减少因 Goroutine 等待执行带来的延迟

GMP 特点

Go 语言的并发性能和可伸缩性得到了最大化的利用。
当 CPU 资源充足时,它可以在 Goroutine 和 P 之间平衡负载,并在适当的时候调度 Goroutine 到合适的 M 上以保证运行效率

Go 协程调度为什么优于线程

多进程/多线程问题

进程/线程数量越多,切换成本越大
多进程/多线程的壁垒:高内存占用,高CPU调度切换
同步竞争,如锁等

协程如何设计

N:1 : 无法利用多个CPU,出现阻塞瓶颈
​1:1 :和多线程、多进程无区别,切换协程成本代价昂贵
​M:N :能够利用多核,过度依赖协程调度器的优化和算法

如何高效

复用线程

避免频繁的创建、销毁线程,而是对线程的复用

Work stealing机制 (work偷窃)

当本线程的P队列中没有G可用并且全局队列为空时,会主动去其他线程的P队列中获取,避免线程的销毁

handle off 机制

当本线程的G运行阻塞时,会释放绑定的P队列,把P队列转移到其他空闲的线程上去

利用并行

GOMAXPROCS设置P的数量,最多有GOMAXPROCS个线程分布在CPU上运行

抢占

一个Goroutine 最多占用CPU 10ms 就会交给其他协程,防止其被饿死
Go 语言的抢占是基于协作式调度的,而切换的主动权在 Goroutine 执行上

  • 通过代码的设计来主动让 Goroutine 放弃控制权,以便让其它 Goroutine 获得执行时间片
    使用 runtime.Gosched() 主动让当前 Goroutine 放弃 CPU 控制权,使当前 Goroutine 立即进入就绪状态,并重新排队等待下一次调度
  • 内部编写超时限制的代码来防止 Goroutine 长时间执行

Go如何调整协程优先级?
在 Go 语言中,所有 Goroutine 都有相同的默认优先级,不能直接通过代码来调整 Goroutine 的优先级
这个是不是一个协程的缺点?我觉着是

全局G队列

当M执行work stealing 机制时,无法从其他P中获取G,就会从全局队列中获取

总结

G是买家,M是卖家,M根据最近的行情,动态调整,也可以去抢其他M的活
G呢,买的东西也不知道是哪个M操办的,G之间是公平的

参考文档

https://juejin.cn/post/7119307307728994335

更多推荐

react 通过ref 获取对应 dom 的位置信息

需求:在点击某个dom元素的时候滚动条要同步滚动进程:获取ref打印,打印出来是一个dom元素,看不到有什么方法查找dom属性信息找到了两个参数,offsetLeft:返回元素的水平偏移位置。offsetTop:返回元素的垂直偏移位置。这两个参数都是基于父节点的,可以直接使用this.cellMainBox.curre

OceanMind海睿思入选弯弓研究院《2023中国营销技术生态图谱8.0》

近日,由国内MarTech领域知名机构弯弓研究院主办的第五届营销数字化大会暨营销科技MarTech交易展在广州成功召开。本次大会发布了《2023中国营销技术生态图谱8.0版》(以下简称“弯弓图谱8.0”),中新赛克海睿思凭借成熟的技术实力成功入选弯弓图谱8.0的中台板块,成为数据与分析领域本土优秀代表服务商之一。随着企

Linux 共享内存

#include<sys/ipc.h>#include<sys/shm.h>intshmget(key_tkey,size_tsize,intshmflg);功能:创建一个新的内存段或者获得一个既有的共享内存段的标识。新创建的内存段中的数据都会被初始化为0参数:-key:key_t类型是一个整型,通过这个创建或者找到一

Linux查看程序和动态库依赖的动态库

一.前言在一些时候,我们需要知道一个程序或者动态库所依赖的动态库有哪些。比如,当我们运行一个程序的时候,发现可能会报错,提示找不到某个符号,这时我们就需要知道程序依赖了什么库,从而添加对应需要的动态库。等等,接下来介绍两种方法查看程序和动态库所依赖的动态库。二.方法介绍1.使用objdump命令objdump-pFIL

Toaster - Android 吐司框架,专治 Toast 各种疑难杂症

官网https://github.com/getActivity/Toaster这可能是性能优、使用简单,支持自定义,不需要通知栏权限的吐司想了解实现原理的可以点击此链接查看:Toaster源码集成步骤如果你的项目Gradle配置是在7.0以下,需要在build.gradle文件中加入allprojects{repos

(总目录)springboot - 实现zip文件上传并对zip文件解压, 包含上传oss

全文目录,一步到位1.本文概述1.1本文简介2.功能实现2.1统一文件校验2.2普通(多)文件上传[服务器]2.2.1controller层2.2.2service层2.2.3业务impl实现类2.2.4FileIOUtils工具包代码2.3zip文件的解压2.4图片文件的压缩2.5oss文件后端上传2.6oss文件前

九、【漏洞复现】Struts 2 远程代码执行漏洞s2-046(CVE-2017-5638)

九、【漏洞复现】Struts2远程代码执行漏洞s2-046(CVE-2017-5638)9.1、漏洞原理Struts2是一个基于MVC设计模式的Web应用框架,本质上相当于一个Servlet,在MVC设计模式中,Struts2作为控制器来建立模型与视图进行数据交互。攻击者通过发送恶意构造的HTTP数据包利用该漏洞(通过

vue 把echarts封装成一个方法 并且从后端读取数据 +转换数据格式 =动态echarts 联动echarts表

1.把echarts在methods封装成一个方法mounted在中调用折线图和柱状图mounted调用下边两个方法mounted(){//最早获取DOM元素的生命周期函数挂载完毕console.log('mounted-id',document.getElementById('charts'))this.line()

Comparator 接口使用方法,结合java8新特性及源码分析

目录1Comparator介绍1.1函数式声明1.2简单的小案例2.Comparator中的方法2.1compare抽象方法例子2.2comparing方法源码参考解释详细解释<?superT,?extendsU>讲解comparing代码样例例子comparing中的方法源码分析<T,UextendsComparab

安防视频/视频汇聚平台EasyCVR使用onvif探测添加设备通道详细步骤来啦!

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同,支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。音视频流媒体视频平台EasyCVR拓展性强,视频能力丰富,具体可实现视频监控直播、视频轮播、视频录像、云存储、回放与检索、智能告警、服务器集群、语音对讲、云台控制、电子地图、H.2

Nginx

概念nginx什么是nginxnginx是一个高性能的HTTP和正、反向代理的服务器nginx可以干什么可以作为web服务器,可以实现反向代理,可以实现基于反向代理的负载均衡,可以作为静态资源服务器(类似于七牛云)什么时候使用nginx后台搭建集群的时候,访问量较大,使用使用nginx作为静态资源的web服务器-部署前

热文推荐