【go基础】2.GMP调度模型-程序员宅基地

技术标签: golang  go语言原理  后端  

目录

进程和线程

1. 单进程

2. 多线程/进程

3. 将内核线程与用户线程的区分开来

4. golang的协程goroutine

golang的GMP模型

1. 核心思想

复用线程

利用并行

抢占式调度

全局G队列

P队满时入队的情况

自旋线程

休眠的M队列

特殊的M和G

2. 一些疑问

为什么每个P都有一个自己的队列

如果P绑定了一个M正在执行goroutine,此时goroutine有IO操作会怎样?

关于gopark与goready

处理全局缓存的调度器schedt,是怎样调度全局队列的?怎样在G0里运行的?

是否所有P容量相同?


进程和线程


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

1. 单进程

  • 单一的执行流程
  • 进程阻塞造成的cpu浪费

2. 多线程/进程

  • cpu时间片频繁切换,成本高
  • 设计复杂,需要考虑同步问题、锁竞争问题
  • 内存占用高

3. 将内核线程与用户线程的区分开来

内核线程=线程,用户线程=协程(coroutine)

线程的cpu调度是抢占式的,协程的用户态调度是协作式的,一个协程让出cpu后才执行下一个协程。

将线程和协程设计成绑定关系:

    (1)N:1关系,N个协程绑定1个线程

            优点:协程都在用户态切换,不会陷入内核态,切换快。

            缺点:但无法利用多个cpu,某一个协程阻塞会导致其他协程无法进行

    (2)1:1关系,1个协程绑定1个线程

            内存占用高,cpu上下文切换成本高

    (3)M:N关系,多个协程绑定到多个线程

            实现复杂,依赖调度器优化和算法

4. golang的协程goroutine

内存占用小(几kb),可以支持更多并发
调度灵活,虽然一个goroutine的栈只有几kb,但可以在运行时灵活分配

抢占式调度


1、协作式调度(同步线程切换) 

    线程的执行时间由线程本身控制,线程把自己的工作执行完之后,主动通知系统切换到另一个线程上

    优点:实现简单,线程干完事情才切换线程,没有上下文切换操作,省CPU资源。

    缺点:易阻塞,一直占有CPU阻塞不让出使用权,可能会导致整个系统崩溃。

2、抢占式调度(异步抢占)

    线程的切换不由线程本身决定,是系统控制的。也就是说线程本身是没有办法主动获得可控的执行时间的,但是可以让出执行时间。

    时间片10ms。

    优点:不易阻塞

    缺点:因为线程切换由系统控制,线程上下文切换会比较频繁,消耗比较多的CPU资源

3、go使用两级线程模型的GMP模型,依赖调度器的优化和算法,保证了既不阻塞也减少cpu切换的浪费

golang的GMP模型


参考:
G:协程(用户线程)
M:内核线程
P:调度器

1. 核心思想

为了解决多线程 M 对应多协程 G 带来的锁竞争导致的性能问题、cpu切换问题,golang设计了一个调度器P,通过调度策略进行资源分配,为协程G合理分配内核线程M。GMP设计了抢占、Work Stealing、Hand off等机制,主旨是尽量复用线程,避免线程的频繁创建和销毁;提高并行能力;减小内存占用。

复用线程

work stealing:P无可用G时,从其他P偷取一半
hand off:G阻塞时,M会释放P,M和G绑定在一起,P会去寻找其他空闲M去绑定

利用并行

    P可以设置多个,一般值是内核数,可以环境变量设置

抢占式调度

    P执行G的时间片为10ms,让出cpu避免其他G饿死

全局G队列

    P没有G时,优先从全局队列取

P队满时入队的情况

    从队头取一半的G,和新的G放在一起,打乱顺序,放到全局G队列中
    源码runtime/proc.go: runqputslow()

自旋线程

    P队空时,由G0进程调度,称M为自旋线程

休眠的M队列

    M没有可绑定的P时,会进入休眠队列,等待新的P来绑定,长时间休眠会被gc

特殊的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

    main函数的协程是G1

2. 一些疑问

为什么每个P都有一个自己的队列

  • 减少锁竞争:如果只有一个全局队列,那么所有M在获取新的goroutine时都需要对该队列进行访问,这会导致锁竞争的问题。而每个P拥有自己的本地队列,可以减少锁的使用和竞争,从而提高并发性能。
  • 负载均衡:每个P都拥有自己的本地队列,当P上的M执行完当前goroutine后,可以直接从本地队列中取出下一个goroutine来执行,而无需与其他P的队列进行交互。这有助于实现负载的均匀分布,避免某些P上的goroutine过多或过少。
  • 可伸缩性:随着goroutine数量的增加,GMP模型可以动态地调整P的数量来适应负载变化。每个P的本地队列可以独立地管理其上的goroutine,使得整个系统更加灵活和可扩展
  • 局部性原理:将goroutine按照P进行分组,使得同一个P上的goroutine更有可能被连续调度执行。这有助于减少线程切换和上下文切换的开销,提高缓存命中率,从而提高程序的性能。

如果P绑定了一个M正在执行goroutine,此时goroutine有IO操作会怎样?

  • goroutine挂起:检测IO阻塞操作,goroutine挂起加入等待队列中,避免M在这个G上浪费时间
  • M与P解绑:允许P去执行其他goroutine
  • M寻找其他工作:解绑后的M会尝试绑定其他P。如果找不到,M可能会进入睡眠状态,等待新的工作到来
  • IO完成与goroutine唤醒:当IO操作完成后,P会绑定一个新的M来执行这个goroutine。

关于gopark与goready

  • gopark:将G变为等待状态,等待唤醒
  • goready:将G唤醒,变为可运行状态

处理全局缓存的调度器schedt,是怎样调度全局队列的?怎样在G0里运行的?

TODO

是否所有P容量相同?

从缓存区取G的过程,如果本地P为空,从调度器中取P容量的一半个G,放到自己的P里,根据work stealing,偷取的是其他P的一半,所以是否所有P容量相同??TODO

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/lixiaonan0318/article/details/136951146

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法