美文网首页
【Go 精选】从 GM 到 GMP 模型

【Go 精选】从 GM 到 GMP 模型

作者: 熊本极客 | 来源:发表于2021-09-29 22:01 被阅读0次

本文简单介绍了 GM 和 GMP 模型,其中分析了 GMP 针对 GM 存在问题的优化点。

1 GM 模型

GM 模型中的 G 全称为 Goroutine 协程M 全称为 Machine 内核级线程

全局队列存放协程 G,同时 M 内核级线程会从全局队列中获取 G。
GM 模型的缺点:① 全局队列的锁竞争,当 M 从全局队列中添加或者获取 G 的时候,都需要获取队列锁,导致激烈的锁竞争。② M 转移 G 消耗较多资源,当 M1 在执行 G1 的时候, M1 创建了 G2,为了继续执行 G1,需要把 G2 保存到全局队列中,而 M2 刚好获取 G2 来执行。因为 M1 原本就保存了 G2 的信息,所以 G2 最好是在 M1 上执行。

GM 模型.png

2 GMP 模型

GMP 模型中的 G 全称为 Goroutine 协程M 全称为 Machine 内核级线程P 全称为 Processor 本地队列。对比 GM 模型, GMP 模型新增了本地队列 P。

M1 新增的 G 会优先被保存在 M1 所绑定的本地队列 P1 中,其中 P1 最多存放 256 个 G。如果本地队列 P1 满了,会把新增的 G 存放在全局队列中(加锁操作)。最终 M1 会从本地队列 P1 获取 G 进行调度(无锁操作)。

GMP 模型.png

问题1:M 如何从 P 中获取 G
M 从 P 中获取 G 的顺序:本地队列 P 》全局队列 P 》偷别人的 P 队列,具体流程如下。
① M0 首先从本地队列 P 中获取 G(无锁操作)
② 如果本地队列 P 为空,则从全局队列 P 获取G(加锁操作)
③ 如果全局队列 P 为空,再去另一个本地队列 P 采用 work-stealing 算法偷取一半数量的 G。

问题2:当 M0 在执行 G1 的时候被阻塞了,如何继续执行

P 的数量是由环境变量 $GOMAXPROCS 或者由 runtime 的 GOMAXPROCS() 方法,其创建时机是系统运行的时候;M 的数量默认等于内核线程数,其创建时机是由内核管理,即当没有足够的 M 关联 P /原来的 M 被阻塞,内核会创建新 M 去关联 P。

如下图所示,当 M0 在执行 G1 的时候阻塞了,M0 与 P 解绑,接着 M1 绑定 P,然后执行 G2。

M 阻塞场景.png

问题3:G 从创建、保存、被获取、调度和执行、阻塞的生命周期

步骤 1:创建 G,关键字 go func() 创建 G;
步骤 2:保存 G,创建的 G 优先保存到本地队列 P,如果 P 满了,则会保存到全局队列中;
步骤 3:M 获取 G,M1 首先从本地队列 P1 获取 G,如果 P1 为空,则从全局队列获取 G,如果全局队列也为空,则从另一个本地队列偷取一半数量的 G;
步骤 4:M 调度和执行 G,M1 调用 G.func() 函数执行 G,如果 M1 在执行 G 的过程被阻塞了,则本地队列 P1 与 M1 解绑。利用 hand-off 机制,新内核线程 Mx 绑定 P1,接着继续执行 P1 中其余的 G。

G 生命周期.png

3 GM 与 GMP 的区别

从设计角度看,GMP 对比 GM 增加了一层本地队列 P。GMP 新增本地队列 P 后,如何优化 GM 存在的问题 —— ①全局队列锁竞争激烈;②M 转移 G 消耗较多资源

区别 1:减少大量的全局队列锁竞争。M 绑定本地队列 P 后,直接在 P 中获取、添加和执行 G(无锁操作)。

减少锁竞争.png

区别 2:提高资源利用率 —— 尽量在同 1 个 M 中创建和执行 G。M 创建完 G 后,会将该 G 保存在与 M 绑定的本地队列 P 中,后续 M 会从 P 中获取和执行 G。因为 G 的信息在创建时保存到 M 中,所以在后续执行的过程中不需要转移 G 的信息,即不涉及线程上下文切换。

区别 3:提高资源利用率 —— 获取全局队列的 G 和 work-stealing 偷其它本地队列 P 一半数量 G。当 M0 的本地队列为空,M0 先去全局队列获取 G,如果全局队列为空,再其它本地队列偷一半数量的 G。

work-stealing 机制.png

区别 4:提高资源利用率 —— 利用 hand-off 机制处理阻塞问题。当 M0 执行 G1 的时候被阻塞了,内核系统会调度 M1 重新绑定本地队列 P,然后继续执行 G2。

hand-off 机制.png

注意:如何处理本地队列 P 中的每个 G 都被阻塞的极端场景
由于 hand-off 机制会使得 M 和 P 在成对扩增,为了限制无限扩增的极端场景,P 通过 runtime.GOMAXPROCS() 设置最大值,默认值等于 CPU 核数。

G 阻塞的极端场景.png

相关文章

  • 【Go 精选】从 GM 到 GMP 模型

    本文简单介绍了 GM 和 GMP 模型,其中分析了 GMP 针对 GM 存在问题的优化点。 1 GM 模型 GM ...

  • go 调度器实现

    GO 语言的调度器 目录 GMP 模型简介 调度器实现机制 GMP 模型简介 先来一张经典的GMP 关系图 G 是...

  • Go - GMP模型

    简述 G — 表示 Goroutine,它是一个待执行的任务; M — 表示操作系统的线程,它由操作系统的调度器调...

  • go并发的那些事

    思考:go为什么那么擅长并发? 答:从设计理解上来讲我觉着golang的CSP并发模型与GMP调度器是基石。你看虽...

  • golang协程调度面试总结

    1.go的GMP模型 goroutine运行在用户态,是由runtime来控制调度的,调度过程中主要涉及到三个对象...

  • Go GMP

    一文彻底弄懂go中的调度GMP先说,协程的本质是用户态的线程,用户对其有控制权限,内存占用少,切换代价低。 再来解...

  • gmp模型

    1、G G是Goroutine的缩写,相当于操作系统中的进程控制块,在这里就是Goroutine的控制结构,是对G...

  • go 的并发调度(一) GMP 模型

    协程和线程的历史关系? 抢占式和协同式抢占式就是线程无法决定自己执行多久,由操作系统(或其他分配系统)来分配一个线...

  • golang内存管理之内存分配

    一、知识准备 GMP运行时调度模型 go原生支持并发,不需要像Java那样需要显示地开启一个线程,也不像Pytho...

  • 一文入门 Go 的性能分析

    Go 为了实现更高的并发,自己实现了用户态的调度器,称之为 GMP 模型,在上一篇文章中,我们已经简单分析了它的实...

网友评论

      本文标题:【Go 精选】从 GM 到 GMP 模型

      本文链接:https://www.haomeiwen.com/subject/glefnltx.html