go中的线程的实现模型-P G M的调度

2023-11-03

线程实现模型

 

go中线程的实现是依靠 P G M

M machine的缩写。一个M代表一个内核线程,或称工作线程

P processor的缩写。一个P代表执行一个Go代码片段所需要的资源(或称上下文环境

G goroutine的缩写。一个G代表一段Go代码片段。前者是对后者的一种封装。

 

可能存在的调度关系

 

 

1、M

 一个M代表一个内核线程。在大多数情况下,创建一个M,都是由于没有足够的M来关联P并运行其中可运行的G。不过,在运行时系统执行系统监控或垃圾回收等任务的时候,也会导致新M的创建。下面是M的部分结构。

 

 

上面是M中重要的几个字段。

1g0标识一个特殊的goroutine。这个goroutineGo运行时系统在启动之初创建的,用于执行一些特殊的额任务。

2mstartrn标识M的起始函数,这个函数就是我们在编写go语句携带的那个函数。

3curg会存储当前M正在运行的那个G的指针

4p额值会指向与当前M相关联的那个P

5nextp字段用于暂存与当前M有潜在关联的P。把调度器将某个P赋值给某个Mnextp字段的操作称为MP的颈联。运行时系统有时候会把刚刚重新启用的M和已与他颈联的那个P关联在一起,这也是nextp字段的主要作用。

6、字段spinningbool类型,它表示这个M是否正在寻找可运行的G。在寻找过程中,M会处于自旋的状态。

 

M在创建之初就会加入到全局(runtime.allm)的M列表。这时,它的起始函数和预联P也会被设置。最后,运行时系统会为这个M专门创建一个新的内核线程与之相关联。起始函数仅当运行时系统要用此M执行系统监控或垃圾回收等任务的时候才会被设置。全局M列表,运行时系统需要他的时候,会通过它获取所有M的信息。同时,它也可以防止M被当做垃圾回收掉。

 

在新的M被创建之后,Go运行时系统会对他进行一番初始化,其中包括对自身所持的站空间以及信号处理方面的初始化。在这些初始化工作都完成之后,该M的起始函数会执行(如果存在的话)。如果,这个起始函数代表的是系统监控任务的话,那么该M会一直执行它,而不会继续后面的流程。否则,在起始删除执行完毕之后,当前M将会与那个预联的p完成关联,并准备执行其他的任务。M会依次在多处寻找课运行的G并运行。

 

运行时系统管辖下的M(或者runtime.allm中的M)有时候也会被停止,比如在运行时系统执行垃圾回收任务的过程中。运行时系统正在停止M的时候,会把它放入调度器的空闲M列表(runtime.sched.midle)。在需要一个未被使用的M的时,运行系统会先尝试从该队列中获取。M是否空闲,仅以它是否存在于调度器的空闲M列表中为依据。

 

单个GO程序使用的M的数量是可以设置的。go启动的时候会先启动一个引导程序,这个引导程序会为其创建必要的环境。其中包括M的最大值,初始值是1000。但是实际情况这么多的线程是很难共存的。

 

新设置的值不能小于正在运行m的个数。

2、P

 

Go的运行时系统会适时的让P与不同的M建立或断开关联,以使p中的那些可运行的G能够获得运行时机,这与操作系统内核在cpu之上实时的奇幻不同的线程或进程的情况类似。

 

改变单个Go程序间接拥有的P的最大量有两种方法。1、调用函数runtime.GOMAXPROSS并把想要设定的数量作为参数传入。2、运行前设置环境变量GOMAXPROSS的值。(这个值一般初始化设定是cpu的核数)但是我们需要注意的尽量在go初始化的时候进行设置。一个G被启动后,会被追加到某个P的可运行的G队列中。如果当前M因为系统调用而阻塞(更确切的说,是它运行的G进入了系统调用)的时候,运行时系统会把M和它关联的P分离开。如果这个P的可运行的队列中还有可执行的G,那么运行时系统会找到一个空闲的M,或创建一个新的M,并和P关联。因此,有很多时候M的数量是大于P的数量的。  

 

确定完成P最大量之后,运行时系统会根据这个数值重整全局的P列表(runtime.allp)。于全局的M;列表类似,该列表中包含了当前运行时系统创建的所有P。运行时系统会把这些P中可运行的G全部取出,并放入调度器的可运行G队列中。这是调整全局全局P列表的一个重要条件。被转移的哪些G,会在以后经由调度再次放入某个P的可运行G队列。

 

与空闲M列表类似,运行时系统也存在一个调度器的P列表(runtime.sched.pidle)。但一个P不在于任何M关联的时候,运行时系统就会把它放到该列表;而当运行时系统需要一个空闲P关联任何M的时候,会从次列表中取出一个。P进入空闲P列表的一个前提条件是它的可运行的G队列必须为空。

 

M不同的是P是有状态的

 

1Pide 此状态表示当前P未与任何的M存在关联

2Prunning 此状态表示当前P正在与某个M关联

3Psyscall   此状态表示当前P的运行的那个G正在进行系统的调用。

4Pgcstop   此状态表明运行时系统需要停止调度。例如,运行时系统在开始垃圾回收的某步骤前,就会试图把全局的P列表中的所有P都至于此状态。

5Pdead  此状态表明当前P已经不会在被使用,如果Go程序运行的过程中,通过runtime.GOMAXPROCS函数减少了P的最大量,那么多余的P就会被运行时系统至于此位置。

 

 

 

P在创建之初的状态是Pgcstop。持续的时间很短暂,马上就会变成Pidle

 

每个P中除了都有一个可运行的G队列外,海都包含了一个自由G列表。这个列表中包含了一些已经运行完成的G。随着运行完成的G的增多,该列表会很长。如果它增加到一定的长度,运行时系统就会把其中的部分G转移到调度器的自由G列表中。另一方面,当使用go语句欲启动一个G列表的时候,运行时系统会先试图从相应P的自由列表中获取一个现成的G,来封装这个Go语句携带的函数(也称GO函数),仅当获取不到这样的一个自由G的时候,运行时系统会在发现其中的自由G太少时,预先尝试从调度器自由G列表中转译过来一些G。所以,只有在自由G列表页完全为空的情况下,才会有新的G被创建。这样大大提高了G的复用率。

 

3G

一个G代表的是一个goroutine

 

Go的编译器会把go语句变成内部函数newproc的调用,并把go函数及其内部的参数传递给这个函数。

 

运行时系统接到这样的一个调用之后,会先检查go函数及其参数的合法性。然后视图从本地的P自由G列表和调度器的自由G列表获取可用的G。如果没有获取到,就新建一个G。与MP相同,运行时系统持有一个G的全局列表。新建的G会第一时间加入到这个列表中。这个全局的列表的主要的作用:集中存放当前运行时系统中所有的G的指针。无论用于封装的这个GO函数的G是否全新,运行时系统都会对他进行一次初始化,包括go函数以及设置G的状态和ID等步骤。在初始化完成之后,这个G会立即被存储到本地Prunnext字段中,该字段用于存储刚出炉的G,以便于更好的运行它。如果这时runnext字段已经存有一个G,那么这个已有的G就会被放到该P的可运行G队列的末尾。如果该队列已满,那么这个G就只能追加到调度器的可运行G队列中了。

1Gidle 表示G刚被新分配,但是还没有初始化。

2Grunnable 表示G正在课运行队列中等待运行。

3Grunning  表示G正在运行。

4Gsyscall    表示G正在执行某个系统调用。

5Gwaiting    表示G正在阻塞。

6Gdead        表示G正在闲置

7Gcopystack  表示当前G的栈正在被移动,移动的原因可能是栈的扩展或收缩。

 

在运行的时候,我们用G封装Go函数的时候,会先对这个G进行初始化。一旦G准备就绪,其状态

就会被设置成Grunnable。一个G真正被使用的时候是在其状态被设置成Grunnable之后。

 

一个G在运行的过程中,是否会等待某个事件以及会等待什么样的事件,这个是由封装的Go函数决定的。

比如说:

1、其中的函数对通道的操作,包括对通道值的接收和发送,那么到对应的代码有可能进入Gwiting2、涉及网络I/O的时候也会导致

3、操控定时器(time.Timer)和调用time.Sleep函数同样也会造成G的等待。在事件到来之后,G会被唤醒到Grnnable状态。然后等待被调度执行。

 

 

 

G在退出系统调用的时候。运行器先尝试直接运行这个G,仅当无法直接运行的时候,才会吧它装换成Grunnable状态发放入到调度器的自由G列表中。 这样在其退出系统调用的时候就立刻被运行大大提高了运行的效率。

 

进入死亡状态(Gdead)的G是可以重新初始化并使用的,但是P进入死亡状态(Gdead)之后就只能被销毁了。

 

调度器

一轮调度

引导程序会为Go建立必要的运行环境。完成了初始化的工作之后,Go程序中的main函数才会执行。

然后会让封装Main函数的G马上有机会运行。封装main函数的G总是Go运行时系统创建的第一个用户G。所以这个G总是最先执行的。(通过main函数创建的G如果一起执行的话,不一定会运行)

 

一轮调度的时候回判断M是否已经和G锁定。如果发现当前M已经和M锁定了,就会暂时阻塞当前的M。一旦与他锁定的G处于可运行的状态,它就会唤醒阻塞的那个G,然后继续运行。那么阻塞当前M意味着相关的内核线程不能在去做其他的事情了。调度器也不会为当前M寻找可运行的G,相当于在浪费资源。如果调度器为当前M找到了一个可运行的G,但发现这个G已经和某个M锁定了,他回去唤醒与之锁定的M易云星该G,并重新为当前M寻找可运行的G

 

如果判断当前M未与任何的G锁定,那么一轮调度的主流程就会继续运行。这时候,调度器会检查运行时系统是否有运行的串行任务正在等待执行。(串行任务的执行需要停止Go的调度器个人猜测串行的执行是一步一步执行,并发的执行会破坏串行的资源)如果有串行的任务,需要停止调度器。字段gcwaitingstopwaitstopnote都是串行运行时任务执行前后的辅助协调手段。gcwaiting表示是否需要停止调度,在停止之前这个值会被设置为1;再恢复调度之前,该值会被设置为0。这样主要调度器发现gcwaiting的值为1,就会把P的状态设置为Pgcstop,然后自减stopwait字段的值。如果发现自减后的值为0,就说明所有P的状态都已为Pgcstop。这时候就可以利用stopnote字段,唤醒所有等待中的M

 

Go调度并不是运行在某个专用的内核线程中的程序,调度程序会运行在若干已存在的M(或者内核线程)中。调度的时候运行系统中几乎所有的M都会参与调度任务,它们共同实现了Go调度器的功能。

 

全力查找可运行的G

 

调度器如果没有发现可运行的G的时候就会进入全力查找可运行G”的子流程。概括的说就是,这个子流程会多次尝试从各处搜索可运行的,甚至从别的P红偷取可运行的G

1、获取执行终结器的G

2、从本地P的可运行G队列获取G

3、从调度器的可运行G队列获取G

4、从网络I/O轮训器(或称netpoller)处获取G

5、从其他P的可运行G队列获取G

6、获取GC标记任务的G

7、从调度器的可运行G队列获取G

8、从全局P列表中每个P的可运行G队列获取G

 

网络I/O轮训器(即netpoller)是Go为了操作系统提供异步I/O基础组件之上,实现自己的阻塞式I

/O而编写的子程序。Go所选用的异步I/O基础组件都是可以高效执行网络I/O(比如epollkqueue

当一个G视图在一个网络连接上进行读、写操作的时候,底层程序(包括基础组件)就会开始为此做准备,此时G就会被转入Gwaiting状态。一旦准备好了,基础组件就会返回相应的事件,就会让netpoller立即通知为此等待的G(?是不是阻塞的G都会进入Gwaiting呢)

 

G的自旋,当M还没有找到G来运行。直到找到了可运行的G,或者始终未找到G而需要停止M,当前M就会退出自旋的状态。一般情况下,运行时系统中至少会有一个自旋的M,调度器会尽量保证有一个自旋的M存在。

 

启动或停止M

 

1stopm()。停当前M的执行,直到有心的G变的可运行而被唤醒。

2gcstopm()。为串行运行时任务的执行让路,停止当前M的执行。串行运行时任务执行完毕之后会被唤醒。

3stoplockedm()。停止与某个G锁定的当前M的执行。直到整个G变的可运行而被唤醒、

4startlockedmgp *g)。唤醒与gp锁定的那个M,并让该M去执行gp

5startm_p_ *p,spinning bool)。唤醒或创建一个M区关联_P_并开始执行。

 

转载于:https://www.cnblogs.com/ricklz/p/10953173.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

go中的线程的实现模型-P G M的调度 的相关文章

  • 检查 Java 版本时出错:找不到 java.dll

    为什么我会得到这个 我该如何修复它 C Users ash gt java version Error Registry key Software JavaSoft Java Runtime Environment CurrentVersi
  • 什么是运行时环境?

    有人可以用简单的术语解释一下这意味着什么吗 是指应用程序代码运行的环境 DOS Windows Linux 等 吗 将其与开发环境和构建环境区分开来 您往往会在这里找到层次结构 运行时环境 执行程序所需的一切 但没有工具可以更改它 构建环境
  • 如何在运行时替换 Moose 对象的方法?

    是否可以在运行时替换 Moose 对象的方法 通过查看源代码Class MOP Method http search cpan org perldoc Class MOP Method which Moose Meta Method htt
  • 查找关联程序以使用 Java 打开文件

    我希望使用计算机上安装的关联程序 在本例中使用 MS Word 或 Open Office Writer 从 Java 应用程序打开文件 比如说 word 文档 问题是我想等到这个子进程完成 这可以使用 Process 类中的 waitFo
  • 从内核空间到用户空间的事件通知

    当内核空间发生事件时如何通知用户空间应用程序 当数据到达某个 GPIO 时 硬件会生成中断 该数据被复制到内核缓冲区 此时 我希望驱动程序通知应用程序它可以调用read函数将数据从内核缓冲区复制到用户空间缓冲区 我想用epoll方法 但是e
  • Big O:如何根据外部 for 循环确定 for 循环增量的运行时间?

    我有以下算法 运行时复杂度为 O N 2 但我想对其有更深入的了解 而不是仅仅记住常见的运行时 分解和分析它的正确方法是什么i 1考虑在内层 for 循环中吗 void printunorderedPairs int array for i
  • Gradle compileKotlin includeRuntime 未将运行时添加到 jar

    我有一个 Kotlin Gradle 项目 我想在 jar 文件中包含 Kotlin 的运行时和 stdlib 我目前正在使用它 但当我使用 build gradle 配置构建项目时 它不包括运行时或 stdlib compileKotli
  • 提升 Asio 单线程性能

    我正在实现需要维护大量 100K 或更多 长期连接的自定义服务器 服务器只是在套接字之间传递消息 并且不进行任何认真的数据处理 消息很小 但每秒都会接收 发送许多消息 减少延迟是目标之一 我意识到使用多核不会提高性能 因此我决定通过调用在单
  • epoll 与 select 对于极少量的连接

    我一直使用 select 来处理连接 最近我们的套接字库发生了变化 select 被 Linux 平台的 epoll 取代 我的应用程序架构是这样的 我只建立一个或最多 2 个套接字连接 并在单个线程中对它们进行 epoll select
  • 如何以编程方式设置或清除 32BIT 标志?

    编译时 我总是将其设置为 Any CPU 然而 有些客户没有所需二进制文件的 64 位版本 即使在 x64 系统上运行也是如此 在这些情况下 我要求他们使用 corflags exe 32BIT 选项修改我的二进制文件 http msdn
  • 使用 System.setOut() 重定向 Runtime.getRuntime().exec() 输出;

    我有一个程序 Test java import java io public class Test public static void main String args throws Exception System setOut new
  • 调试时静态变量初始值设定项的运行时评估中的奇怪现象

    当我从 Visual Studio 内部启动并附加调试器 F5 时 我的应用程序运行良好 但是当我启动应用程序而不附加调试器 Ctrl F5 或启动 exe 文件 时 我总是得到一个StackOverflowException幸运的是 它被
  • spring 在运行时添加数据源

    我正在开发一个带有每个租户数据库策略的 Spring Boot 多租户应用程序 要求是在运行时添加新数据库 这意味着我必须动态创建新的数据源对象 我还研究了 Spring 的 AbstractRoutingDataSource 但需要预定义
  • 无法调试,致命信号 6 (SIGABRT) 位于 0x00007c37(代码=-6)

    我遇到了以下情况 无法调试 Android 应用程序 我可以运行它 但无法调试它 我开始了全新的项目 复制了所有内容并且它有效 意味着能够调试 但又得到了这个 我在模拟器和手机上都尝试过 但没有成功 我使用的是Android Studio
  • 使用 eval 加载模块

    我在 Perl 和内置函数方面遇到了一些麻烦eval http perldoc perl org functions eval html 我浏览了网络 但找不到任何答案或示例代码 我想动态加载模块 在执行时间之前我不知道它们 module
  • 应用程序启动时立即隐藏导航栏

    基于以下代码片段 我能够隐藏状态栏当应用程序启动时 但不是导航栏 由后退 主页和任务管理器按钮组成的栏 因为它隐藏了稍后在 MainActivity 的线程完成加载后 这是清单
  • “未定义的行为”是否会扩展到编译时?

    我们都听过这样的警告 如果你调用未定义的行为在 C 或 C 中 任何事情可以发生 这是否仅限于任何运行时行为 或者这还包括任何编译时行为吗 特别是 编译器在遇到调用未定义行为的构造时是否允许拒绝代码 在标准中没有其他要求的情况下 甚至崩溃
  • Runtime.exec 处理包含多个空格的参数

    我怎样才能进行以下运行 public class ExecTest public static void main String args try Notice the multiple spaces in the argument Str
  • 哪些属性有助于运行时 .Net 性能?

    我正在寻找可用于通过向加载器 JIT 编译器或 ngen 提供提示来确保 Net 应用程序获得最佳运行时性能的属性 例如我们有可调试属性 http msdn microsoft com en us library k2wxda47 aspx
  • 确定 Objective-C 方法在运行时是否是可变的

    有没有办法在运行时找出给定方法是否是可变参数类型 就像是method getTypeEncoding 这不会告诉我一个方法是否接受可变数量的参数 或者有什么技巧可以告诉我们吗 罗伯特的评论是正确的 考虑 interface Boogity

随机推荐

  • 查询三方jar包漏洞

    安全检测的时候一般会对第三方的jar包扫描 下面的地址可以根据artifactId 或者漏洞查询相应的不同版本漏洞 然后自己做相应的处理 一般都是升级包 或者直接用功能类似的包替换 避免重大事故 nvd漏洞查询
  • MySQL数据库——数据库和表的基本操作(一)

    目录 第1关 查看表结构与修改表名 一 本关任务 修改表名 并能顺利查询到修改后表的结构 查看数据表基本结构 查看数据表详细结构 修改表名 二 编程要求 三 预期输出 四 代码 第2关 修改字段名与字段数据类型 一 本关任务 修改表中的字段
  • 2020牛客暑期多校训练营(第八场)E Enigmatic Partition —— 找规律,差分上差分,有丶东西

    This way 题意 定义合法序列 n a1 a2 am m的大小是你自己构造的 m gt 3 并且满足以下条件 定义f n 为构造n的合法序列的情况数 然后每次问你n为l r中所有数的f的和是多少 题解 其实就相当于要预处理每个数有多少
  • Windows使用串口API函数串口编程

    Windows使用串口API函数串口编程 前言 1 打开串口 1 1 参数详解 1 2 代码示例 1 2 1 获取串口号 1 2 2 打开串口 同步通信 1 2 3 打开串口 异步通信 2 关闭串口 3 配置串口 3 1 配置输入输出缓冲区
  • 20230705

  • python将生成的数据按类别以不同颜色作散点图

    遇事不决 可问春风 春风不语 即随本心 文章目录 文章目录 前言 一 实例 1 读取数据 2 创建空数组 3 数据处理 4 绘制散点图 总结 前言 我们在处理分类问题时 经常需要用图表的形式将数据表现出来 这样会更直观的了解分类效果 一 实
  • nodejs的加密方式

    nodejs的加密方式 一 加密算法 为了保证数据的安全性和防篡改 很多数据在传输中都进行了加密 加密可分为三大类 对称加密 非对称加密 摘要算法 二 对称加密 采用单钥密码系统的加密方法 同一个密钥可以同时作用信息的加密和解密 该方法称为
  • C++中使用tuple

    本文讨论的是在C 11标准下使用tuple 而不是python语言 说到tuple 肯定会第一时间想到python语言 但tuple也不仅仅只在python中有 在C C 等语言中都有这样的数据结构 在C 中的tuple和python语言中
  • 解决VS无法识别手动创建的app.manifest文件的问题

    解决VS无法识别手动创建的app manifest文件的问题 解决方案 删除手动添加的app manifest文件 修改项目属性使项目自动添加app manifest文件 操作流程 1 选择当前项目 单击鼠标右键 选择 属性 2 在 属性
  • 语言小型心形图案代码_C语言写一个小程序,胖胖的爱心桃

    学了这么久的C语言 你是不是有很多会写的小玩意了呢 比如说简单的五角星 三角形 等腰三角形 心形之类的 笔者今天发现了个以前写的一个很好玩的小程序分享给大家 画心的C语言 include
  • python 对二维列表的排序

    例如 这样的列表 对它进行排序 第一种 使用lambda对列表中的数据进行排序 如果不懂lambda的可以去百度哦 有很多详细内容 按数字排序 mylist 张三 0 3 李四 0 4 王五 0 8 谢大脚 0 9 谢广坤 0 1 myli
  • edp和edt哪个好_香水edp和edt的区别

    在香水瓶子上 通常会看到edp和edt的标志 它们的具体区别如下 1 含义不同 E D P是Eau de Parfum的缩写 意思是淡香精 而E D T是Eau de Toilette的缩写 意思是淡香水 2 香精浓度不同 E D P的纯香
  • 以图搜图算法java_龙猫数据爬图新姿势:以图搜图

    如果说购物网站近两年有什么新变化 除了商品类别增多以外 以图搜图功能绝对算很重要的一个 看到自己喜欢的东西根本不用问具体信息 随手一拍马上就能在购物网站找同款 识别率相当高 真是方便又快捷 今天我们就来介绍下 这么好用的生产力工具是如何 进
  • Hiredis_API说明

    转 https blog csdn net xumaojun article details 51597468 同步的API接口 redisContext redisConnect const char ip int port void r
  • Qt信号与槽的Connect详解

    QT通过connect关联信号和槽函数 一 槽函数的执行是同步还是异步 在同一个线程中 Qt信号槽的执行是同步的 当一个信号被发射时 槽函数会立即被调用 而不是被放入事件队列中 这是因为在同一个线程中 事件循环和槽函数都是在同一个线程中执行
  • MySQL——索引

    文章目录 1 简介 2 索引的分类 2 1 主键索引 PRIMARy KEY 2 2 唯一索引 UNIQUE KEY 2 3 常规索引 KEY INDEX 2 4 全文索引 FullText 3 测试索引 3 1 创建100万条数据 3 2
  • 2013年8月27日星期一(DEMO7-19窗口的裁剪等)

    OK 现在马不停蹄 结束这个第7章 拖延的时间真长 有6个月了 汗 这个是上次的应用 加上逻辑判断如何画点 并用GetWindowRect 是客户区 实际上这不对 应该是GetClientRect 果然不对 只能是说把图画上了 代码如下 D
  • egret 学习笔记

    1 egret 的res模块新版不在引擎中
  • C#系列-函数

    一 方法 using System using System Collections Generic using System Linq using System Text using System Threading Tasks name
  • go中的线程的实现模型-P G M的调度

    线程实现模型 go中线程的实现是依靠 P G M M machine的缩写 一个M代表一个内核线程 或称 工作线程 P processor的缩写 一个P代表执行一个Go代码片段所需要的资源 或称 上下文环境 G goroutine的缩写 一