【golang】深入理解GMP调度模型

2023-09-15 18:12:13

Goroutine

Go中,协程被称为goroutine,它非常轻量,一个goroutine只占几KB,并且这几KB就足够goroutine运行完,这就能在有限的内存空间内支持大量goroutine,支持了更多的并发,虽然一个goroutine的栈只占几KB(Go语言官方说明为4~5KB),但实际是可伸缩的,如果需要更多内容,runtime会自动为goroutine分配。

Goroutine特点:

  • 占用内存更小(几kb)
  • 调度更灵活(runtime调度)

GMP指的是什么

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

版本1.0之前GM调度模型

调度器把G都分配M上,不同的G在不同的M并发运行时,都需要向系统申请资源,比如堆栈内存等,因为资源是全局的,就会因为资源竞争造成很多性能损耗。为了解决这样的问题go从1.1版本引入,在运行时系统的时候加入p对象,让P去管理这个G对象,M想要运行G,必须绑定P,才能运行P所管理的G。

在这里插入图片描述

GM调度存在的问题:

  1. 单一全局互斥锁(SchedLock)和集中状态存储
  2. Goroutine传递问题(M经常在M之间传递“可运行”的goroutine)
  3. 每个M作内存缓存,导致内存占用过高,数据局部性较差
  4. 频繁syscall调用,导致严重的线程阻塞/解锁,加剧的性能损耗。

GMP模型

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

在这里插入图片描述

  1. 全局队列:存放等待运行的G。
  2. P的本地队列:同全局队列类似,存放的也是等待运行的G,存的数量有限,不超过256个。新建G‘时,G’优先加入到P的本地队列,如果队列满了,则会把本地队列中一半的G移动到全局队列。
  3. P列表:所有的P都在程序启动时创建,并保存在数组中,最多有GOMAXPROCS(可配置)个。
  4. M:线程想运行任务就得获得P,从P的本地队列获取G,P队列为空时,M也会尝试从全局队列拿一批G放到P的本地队列,或从其他P的本地队列偷一半放到自己P的本地队列。M运行G,G执行后,M会从P获取下一个G,不断重复下去。

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

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会被放入全局队列中。

M0
M0是启动程序后的编号为0的主线程,这个M对应的实例会在全局变量runtime.m0中,不需要在heap上分配,M0负责执行初始化操作和启动第一个G,在之后M0就和其他的M一样了。
G0
G0是每次启动一个M都会第一个创建的gourtine,G0仅用于负责调度的G,G0不指向任何可执行的函数,每个M都会有一个自己的G0。在调度或系统调用时会使用G0的栈空间,全局变量的G0是M0的G0

work stealing机制和hand off机制

  • work stealing机制:获取 P 本地队列,当从绑定 P 本地 runq 上找不到可执行的 g,尝试从全局链表中拿,再拿不到从 netpoll 和事件池里拿,最后会从别的 P 里偷任务。
  • hand off机制:当本线程M因为G进行的系统调用阻塞时,线程释放绑定的P,把P转移给其他空闲的M执行。

版本1.2 ~ 1.13 基于协作的抢占式调度

版本1.2~1.13,程序只能依靠Goroutine主动让出CPU资源才能触发调度。

问题:

  • 某些Goroutine可以长时间占用线程,造成其它Goroutine的饥饿
  • 垃圾回收需要暂停整个程序(Stop-the-world,STW),最长可能需要几分钟的时间,导致整个程序无法工作。

版本1.14~至今 基于信号的抢占式调度

在任何情况下,Go 运行时并行执行(注意,不是并发)的 goroutines 数量是小于等于 P 的数量的。为了提高系统的性能,P 的数量肯定不是越小越好,所以官方默认值就是 CPU 的核心数,设置的过小的话,如果一个持有 P 的 M, 由于 P 当前执行的 G 调用了 syscall 而导致 M 被阻塞,那么此时关键点: GO 的调度器是迟钝的,它很可能什么都没做,直到 M 阻塞了相当长时间以后,才会发现有一个 P/M 被 syscall 阻塞了。然后,才会用空闲的 M 来强这个P。通过 sysmon 监控实现的抢占式调度,最快在 20us,最慢在10-20ms才会发现有一个 M 持有 P 并阻塞了。操作系统在 1ms 内可以完成很多次线程调度(一般情况1ms 可以完成几十次线程调度),Go 发起 IO/syscall 的时候执行该 G 的 M会阻塞然后被 OS 调度走,P 什么也不干,sysmon 最慢要10-20ms才能发现这个阻塞,说不定那时候阻塞已经结束了,这样宝贵的 P 资源就这么被阻塞的M 浪费了。

Sysmon 有什么作用?

Sysmon 也叫监控线程,会变动的周期性检查

好处:

  • 释放闲置超过 5 分钟的 span 物理内存;
  • 如果超过 2 分钟没有垃圾回收,强制执行;
  • 将长时间未处理的 netpoll 添加到全局队列;
  • 向长时间运行的 G 任务发出抢占调度(超过10ms的g,会进行retake);
  • 收回因 syscall 长时间阻塞的 P;

GMP调度过程中存在哪些阻塞

  • I/O,select
  • block on syscall(系统调用(syscall)过程中发生了阻塞)
  • channel
  • 等待锁
  • runtime.Gosched()(手动让当前 Goroutine 主动让出执行权)
更多推荐

docker常用命令汇总及解释

Docker是一个开源的应用容器引擎,它允许开发者将应用及其依赖打包到一个可移植的容器中,并发布到任何流行的Linux机器或Windows机器上。以下是一些常用的Docker命令及其解释:1.`dockerpull`:从DockerHub或其他仓库下载镜像。例如:`dockerpullubuntu:latest`,这将

方法区(Method Area)

方法区(MethodArea)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。虽然《Java虚拟机规范》中把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫作“非堆”(Non-Heap),目的是与Java堆区分开来。《Java虚拟机规范

JVM——8.内存分配方式

这篇文章我们来讲一下jvm的内存分配方式目录1.概述1.1jvm运行时数据区1.2堆空间的分代1.3对象分配的整体流程2.具体的内存分配方式2.1指针碰撞法2.2空闲列表法2.3Java虚拟机选择策略3.小结1.概述我们前面在GC那篇文章中写了JVM的内存分配策略,讲述了对象优先在Eden区进行分配等等几条策略,而这里

免备案海外服务器有什么好处?

介绍一:了解海外服务器免备案的优点免备案海外服务器是指在国外搭建网站服务器而不是在国内备案,这种模式可以带来一定的便利。首先,海外服务器免备案可以使网站更加稳定,因为国外网络环境更加稳定,大多数国外服务器性能高,可以保证网站的正常运行,也可以使网站的响应时间更快。其次,海外服务器免备案还可以保证网站数据的安全性。国外服

TCP协议详解

TCP协议特点:面向连接、字节流、可靠传输。面向连接:使用TCP协议通信的双方必须先建立连接,然后才能开始数据的读写。双方都必须为该链接分配必要的内河资源,以管理连接的状态和连接上数据的传输。TCP连接是全双工的,双方的数据读写可以通过一个连接进行。完成数据交换之后,通信双方都必须断开连接以释放系统资源。字节流:发送端

Docker的相关知识介绍以及mac环境的安装

一、什么是Docker大型项目组件较多,运行环境也较为复杂,部署时会碰到一些问题:依赖关系复杂,容易出现兼容性问题开发、测试、生产环境有差异Docker就是来解决这些问题的。Docker是一个快速交付应用、运行应用的技术:可以将程序及其依赖、运行环境一起打包为一个镜像,可以迁移到任意Linux操作系统。运行时利用沙箱机

2023.9.21 组会记录

Robustdiseasemoduleminingviaenumerationofdiverseprize-collectingSteinertrees通过枚举多样的奖励收集斯坦纳树进行鲁棒的疾病模块挖掘疾病模块挖掘方法(DMMM)已被开发出来,将基因表达谱的分析与蛋白质-蛋白质相互作用(PPI)和其他网络中编码的先验

小步快跑,敏捷开发的精髓!

每日站会,两周一迭代,有自己的“ScrumMaster”,就是敏捷实践?No!具备敏捷之形的团队有很多,但是,真正掌握敏捷精髓的,却并不多见。这是因为,敏捷方法属于simplebutnoteasy(简单但并不好做)。结合我这么多年的体会来看,与其说敏捷是一场研发方式的变革,不如说是一场思维方式的变革。今天,结合我在某试

python基于django或flask开发的健身俱乐部网站rix1z

本系统有三个角色:管理员、用户和教练,要求具备以下功能:(1)用户可以浏览主页了解健身课程、健身器材、会员卡信息、新闻公告等信息,并进行在线留言;(2)管理员通过后台管理员界面,实现对用户信息管理,可以查看健身课程、健身器材等信息,让用户实时知道最新的健身俱乐部管理信息;技术栈后端:python+django前端:vu

鼠标不动了怎么办?3招解决问题!

“这是怎么回事呢?我的鼠标怎么会用着用着就突然不动了呢?现在有一些比较重要的工作要处理。请问有什么方法可以快速解决这个问题吗?”随着电脑在我们日常生活和工作中的广泛应用,鼠标是我们操作电脑不可或缺的工具之一。但是,有时候我们可能会遇到鼠标不动的问题,这会影响到我们使用电脑。鼠标不动了怎么办?今天小编就来告诉大家正确的解

软考之软件设计师考试总结(内附资料)

今年5月27日参加的软考,虽然研究生专业已经和计算机无缘了,但是只要想学,就没有什么能够阻挡。参加软考的初衷只是因为,,,辽宁省软考它不要钱,不要钱的证书咱不白嫖一个说不过去,先考下来再说这个证有没有用吧。确定报考后,再从网上找考试资料,忘记从哪里找的资料了反正资料有讲解视频和历年真题,感觉特别有用(分享网盘资料也不知

热文推荐