Go的GMP模型真的“简单”

golang——GMP调度模型详解 GMP调度模型 阅读详情
本文基于64位平台、1Page=8KB、Go1.19

3979c22b1e134f26d43ef5a614a2f7ae.png     

今天开始《Go语言轻松进阶》系列第四章「Goroutine的调度」。

完整地址 https://tigerb.cn/go/#/kernal

前言

关于GMP模型网上已经有很多文章,讲的内容大多都是如下图的逻辑,本系列不再赘述。本系列换个视角,核心搞清楚两个问题:

  • GMP到底是什么?

  • goroutine如何恢复和保存上下文的?

10a0f509b1dcf1b54d9cde22e9fa4225.png

正文开始。

GMP只是结构体

GMP并不是你想象的那么神奇的存在,其实就是普通的结构体,如同你写业务代码定义的结构体一样,如下:

// Goroutine
// 代码位置:go1.19/src/runtime/proc.go
type g struct {
    stack     stack
    //...略...
    gopc      uintptr 
    startpc   uintptr
    sched     struct {
        sp   uintptr
        pc   uintptr
        //...略...
        bp   uintptr
     }
    //...略...
}
// Machine
// 代码位置:go1.19/src/runtime/proc.go
type m struct {
    g0            *g     
    //...略...
    curg          *g
    p             puintptr
    nextp         puintptr
    //...略...

    mOS 
}
// Processor
// 代码位置:go1.19/src/runtime/proc.go
type p struct {
    id          int32
    //...略...
    m           muintptr 
    mcache      *mcache
    //...略...
    runqhead uint32
    runqtail uint32
    runq     [256]guintptr
    runnext guintptr
    //...略... 
    gFree struct {
        gList
        n int32
    }
    //...略...
    mspancache struct {
        len int
        buf [128]*mspan
    }
    //...略...
    gcw gcWork
}

GMP是系统线程运行的代码片段

GMP和你写的业务代码一样,都是由系统线程运行。

4536d451ca0f0491bd5d2971b925c876.png

GMP是类似面相对象的封装

左右滑动查看
类型结构体含义结构体职责
GGoroutine,代表协程1. 封装可被并发执行的函数片段,比如 go func() {// 函数A}()
G-2. 保存函数片段(协程)切换时的上下文信息
G-3. 封装g的栈内存空间,暂存函数片段(协程)执行时的临时变量的
MMachine,和系统线程建立映射,结构体绑定一个系统线程1. 绑定真正执行代码的系统线程,系统线程执行`G`的调度,和被调度的`G`绑定的函数
M-2. 维护P链表(可以从下一个P的队列找G)
PProcessor,和逻辑处理器建立映射1. 维护可执行G的队列(M从该队列找可执行的G);
P-2. 堆内存缓存层(mcache)
P-3. 维护g的闲置队列

G职责解析

接下来,展开关于G展开两个关键问题:

  • G和函数绑定过程

  • G切换上下文过程

G和函数绑定过程

当你使用go关键字执行一个函数时go func(){}():

  1. G和func具体绑定在哪?

  2. G和func何时绑定?

func main() {
    // 使用go 关键并发执行一个函数
    go func() {
        fmt.Println("demo")
    }()
}

G和func具体绑定在哪?

位于g的结构体 g.startpc属性,详细如下:

// Goroutine
// 代码位置:go1.19/src/runtime/proc.go
type g struct {
 //...略...
 gopc      uintptr  // go关键字创建Goroutine的代码位置
 //...略...
 startpc   uintptr // Goroutine绑定的函数代码地址
 //...略...
}

G和func何时绑定?

  1. 当通过go关键字运行一个函数时

  2. 从g的闲置队列获取一个g,并通过g.startpc属性绑定上待执行的函数fn

// 当你用go关键字执行一个函数
// 通过这个函数 绑定 g 和 待被执行的函数fn
func newproc(fn *funcval) {
    gp := getg()
    // 获取使用go关键字调用fn的代码位置
    // 方便fn执行完成之后跳回原代码位置
    pc := getcallerpc()
    systemstack(func() {
         // 绑定过程在这个函数中
         // 下面进一步分析newproc1
         newg := newproc1(fn, gp, pc)
         _p_ := getg().m.p.ptr()
         // 放入本地队列
         // 等待调度
         runqput(_p_, newg, true)
    })
}

// 绑定过程在这个函数中 分析newproc1
func newproc1(fn *funcval, callergp *g, callerpc uintptr) *g {
    //...略...
    newg := gfget(_p_) // 从g的闲置队列获取一个g
    //...略...
    newg.gopc = callerpc // 重点:设置go关键字的位置,便于fn执行完毕跳回原代码位置
    newg.startpc = fn.fn // 重点:这里绑定待被执行的函数fn
    //...略...
    return newg
}

函数绑定过程如下:

d5ba40b5b79b69b125b3da035ac604b1.png


G切换上下文过程

  1. goroutine的上下文信息具体保存在哪?

  2. goroutine的上下文如何切换?

goroutine的上下文信息具体保存在哪?

位于g的结构体 g.sched属性,详细如下:

// Goroutine
// 代码位置:go1.19/src/runtime/proc.go
type g struct {
 stack     stack // 协程栈 执行过程临时变量存放的地方
 sched     gobuf // Goroutine上下文信息 保存在这个结构
    //...略...
}

// Goroutine上下文信息
type gobuf struct {
 sp   uintptr // 栈指针:指向栈顶
 pc   uintptr // 代码(指令)执行位置的地址
 //...略...
 bp   uintptr // 基指针:指向栈基
}

 goroutine的上下文如何切换?

  • g恢复上下文过程

  • g保存上下文过程

g恢复上下文过程

触发调度时:

  1. 找到可执行的g(来源本地队列、全局队列、netpoll list 读或写就绪的g列表)

  2. 把g的上下文g.sched通过汇编代码中的函数gogo恢复到对应的寄存器中

// g的调度方法
func schedule() {
 //...略...
 // 找可执行的g (本地队列、全局队列、netpoll list 读或写就绪的g列表 等)
 gp, inheritTime, tryWakeP := findRunnable() 
 //...略...
 //在这里 继续往下看
 execute(gp, inheritTime)
}

func execute(gp *g, inheritTime bool) {
 //...略...
 // 关键就是通过gogo这个函数 恢复
 gogo(&gp.sched)
}

gogo函数汇编代码,arm64架构示例汇编代码如下:

// void gogo(Gobuf*)
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), NOSPLIT|NOFRAME, $0-8
 MOVD buf+0(FP), R5
 MOVD gobuf_g(R5), R6
 MOVD 0(R6), R4
 B gogo<>(SB)

TEXT gogo<>(SB), NOSPLIT|NOFRAME, $0
 MOVD R6, g
 BL runtime·save_g(SB)

 MOVD gobuf_sp(R5), R0 // 恢复栈指针
 MOVD R0, RSP
 MOVD gobuf_bp(R5), R29 // 恢复基指针
 MOVD gobuf_lr(R5), LR 
 MOVD gobuf_ret(R5), R0
 MOVD gobuf_ctxt(R5), R26
 MOVD $0, gobuf_sp(R5)
 MOVD $0, gobuf_bp(R5)
 MOVD $0, gobuf_ret(R5)
 MOVD $0, gobuf_lr(R5)
 MOVD $0, gobuf_ctxt(R5)
 CMP ZR, ZR 
 MOVD gobuf_pc(R5), R6 // 恢复PC计数器 指向下一个待执行的指令
 B (R6

g恢复上下文过程:

8bbd822aa3bd521301fd2e3cb6c9cf85.png

g保存上下文过程:

其中两个关键函数如下

  1. func save(pc, sp uintptr)触发保存上下文

  2. func mcall(fn func(*g))触发保存上下文

save函数

func save(pc, sp uintptr) {
 _g_ := getg()
 //...略...
 _g_.sched.pc = pc // 保存代码执行位置
 _g_.sched.sp = sp // 保存栈指针
 //...略...

调用func save(pc, sp uintptr)的场景如下:

  • 进入系统调用时

// 进入系统调用
func entersyscall() {
 reentersyscall(getcallerpc(), getcallersp())
}

func reentersyscall(pc, sp uintptr) {
 _g_ := getg()
 //...略...
 // 保存上下文
 save(pc, sp)
 _g_.syscallsp = sp
 _g_.syscallpc = pc
 casgstatus(_g_, _Grunning, _Gsyscall)
 //...略...
}

g保存上下文过程(进入系统调用通过save函数触发):

a04b294801896fb39a2b949ce3dcafb6.png

mcall函数

func mcall(fn func(*g))执行过程中,从g切换到g0,并执行fn。fn内部会执行调度函数shedule(),触发新的调度,下面会举一个例子。

TEXT runtime·mcall<ABIInternal>(SB), NOSPLIT|NOFRAME, $0-8
 MOVD R0, R26 

 MOVD RSP, R0
 MOVD R0, (g_sched+gobuf_sp)(g) // 保存当前g的栈指针
 MOVD R29, (g_sched+gobuf_bp)(g) // 保存当前g的基指针
 MOVD LR, (g_sched+gobuf_pc)(g)// 保存当前g的下一个待执行指令的位置 PC计数器
 MOVD $0, (g_sched+gobuf_lr)(g)

 // 切换到g0,并执行函数fn
 MOVD g, R3
 MOVD g_m(g), R8
 MOVD m_g0(R8), g
 BL runtime·save_g(SB)
 CMP g, R3
 BNE 2(PC)
 B runtime·badmcall(SB)

 MOVD (g_sched+gobuf_sp)(g), R0
 MOVD R0, RSP 
 MOVD (g_sched+gobuf_bp)(g), R29
 MOVD R3, R0 
 MOVD $0, -16(RSP)
 SUB $16, RSP
 MOVD 0(R26), R4
 BL (R4)
 B runtime·badmcall2(SB)

g保存上下文过程(通过mcall函数触发):

f48ae985653e1513f7a8ecb48e86b431.png

调用func mcall(fn func(*g))的场景如下:

  1. Gosched():触发协作&抢占式式调度时

  2. gopark:g从运行状态转换为等待状态时

  3. goexit1()goroutine执行完成时

  4. exitsyscall()退出系统调用时

  5. 等

详细展开,Gosched():触发协作&抢占式式调度时看看,如下

// 触发调度
func Gosched() {
 checkTimeouts()
 mcall(gosched_m)
}

func gosched_m(gp *g) {
 //...略...
 goschedImpl(gp)
}

func goschedImpl(gp *g) {
 //...略...
 // 正在运行状态转变为 可运行状态
 casgstatus(gp, _Grunning, _Grunnable)
 dropg()
 lock(&sched.lock)
 globrunqput(gp) // 放入全局队列
 unlock(&sched.lock)
 // 触发调度
 schedule()
}

func schedule() {
 //...略...
 // 找到下一个可执行的g
 gp, inheritTime, tryWakeP := findRunnable() 
 //...略...
 // 执行下一个g
 execute(gp, inheritTime)
}

func execute(gp *g, inheritTime bool) {
 //...略...
 // 恢复上下文
 gogo(&gp.sched)
}

// gogo汇编代码(arm64架构)
TEXT gogo<>(SB), NOSPLIT|NOFRAME, $0
 //...略...
 MOVD gobuf_sp(R5), R0 // 恢复栈指针
 MOVD gobuf_bp(R5), R29 // 恢复基指针
 //...略..
  • park_m 把g从运行状态转换为等待状态时

func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason waitReason, traceEv byte, traceskip int) {
 //...略...
 mcall(park_m)
}

func park_m(gp *g) {
 //...略...
 casgstatus(gp, _Grunning, _Gwaiting)
 dropg()
 //...略...
 // 触发调度
 schedule()
}

//...略...
// 同上`Gosched()
  • goexit1()goroutine执行完成时

func goexit1() {
 //...略...
 mcall(goexit0)
}

// goexit continuation on g0.
func goexit0(gp *g) {
 //...略...
 // 触发调度
 schedule()
}

//...略...
// 同上`Gosched()`
  • exitsyscall()退出系统调用时

func exitsyscall() {
 //...略...
 mcall(exitsyscall0)
 //...略...
}

func exitsyscall0(gp *g) {
 casgstatus(gp, _Gsyscall, _Grunnable)
 dropg()
 //...略...
 stopm()
 // 触发调度
 schedule()
}

/...略...
// 同上`Gosched()`

g保存上下文过程(通过mcall函数触发):

6d460ccece8a322686b675e3d10b4e79.png

g的完整切换过程:

  • 当前g保存上下文(save/mcall)

  • 当前g切换到g0,g0执行schedule调度,找到新的可执行的g

  • 新的g恢复上下文(gogo)

  • 最后,实际以上操作都是有系统线程运行的。



M职责解析

  1. 绑定真正执行代码的系统线程

  2. 执行G的调度

  3. 执行被调度的G绑定的函数

  4. 维护P链表(可以从下一个P的队列找G)

// Machine
// 代码位置:go1.19/src/runtime/proc.go
type m struct {
 g0            *g     
 //...略...
 curg          *g  // 当前执行的g
 p             puintptr // m绑定的p
 nextp         puintptr // 4. 维护`P`链表(可以从下一个`P`的队列找`G`)
 //...略...

 // 1. 绑定真正执行代码的系统线程
 // 2. 执行`G`的调度
 // 3. 执行被调度的`G`绑定的函数
 mOS 

 //...略...
}

P职责解析

  1. 维护可执行G的队列(M从该队列找可执行的G);

  2. 堆内存缓存层(mcache)

  3. 维护g的闲置队列

// Processor
// 代码位置:go1.19/src/runtime/proc.go
type p struct {
 id          int32
 //...略...
 m           muintptr 
 mcache      *mcache // 堆内存缓存层(`mcache`)
 //...略...
 runqhead uint32 // 1. 维护可执行`G`的队列(`M`从该队列找可执行的`G`);
 runqtail uint32 // 1. 维护可执行`G`的队列(`M`从该队列找可执行的`G`);
 runq     [256]guintptr // 1. 维护可执行`G`的队列(`M`从该队列找可执行的`G`);
 runnext guintptr // 1. 维护可执行`G`的队列(`M`从该队列找可执行的`G`);
 //...略... 
 // 3. 维护g的闲置队列
 gFree struct {
  gList
  n int32
 }
 //...略...
 mspancache struct {
  len int
  buf [128]*mspan
 }
 //...略...
 gcw gcWork
}

总结

再来回头看开篇的两个问题?

  • GMP到底是什么?

  • goroutine如何恢复和保存上下文的?

是不是已经很清晰。

  • 关于问题一,GMP是三个各司其职的结构体,被系统线程运行。

    类型结构体含义结构体职责
    GGoroutine,代表协程1. 封装可被并发执行的函数片段,比如 go func() {// 函数A}()
    G-2. 暂存函数片段(协程)切换时的上下文信息
    G-3. 封装g的栈内存空间,暂存函数片段(协程)执行时的临时变量的
    MMachine,和系统线程建立映射,结构体绑定一个系统线程1. 绑定真正执行代码的系统线程,系统线程执行`G`的调度,和被调度的`G`绑定的函数
    M-2. 维护`P`链表(可以从下一个`P`的队列找`G`)
    PProcessor,和逻辑处理器建立映射1. 维护可执行G的队列(M从该队列找可执行的G);
    P-2. 堆内存缓存层(mcache)
    P-3. 维护g的闲置队列
  • 关于问题二,goroutine恢复和保存上下文过程(具体如下图所示)

  1. 当前g保存上下文(save/mcall汇编)

  2. 当前g切换到g0,g0执行schedule调度,找到新的可执行的g

  3. 新的g恢复上下文(gogo汇编)

    c2e358692d97136826ff4899d2362d10.png


Go轻松进阶系列 更多文章

99dc5031462489acba03e960f9dc138c.png

原创不易,欢迎👏🏻右下角点赞和在看支持,

让作者的辛勤付出得到认可,感谢~

Go 语言 GMP 调度模型解析 轻量级协程,是 Go 并发的基本执行单元。每个 Goroutine 拥有自己的栈空间、程序计数器和状态信息,初始栈大小仅 2KB,可动态扩缩容。M(Machine):对应操作系统线程,负责执行 Goroutine 的代码。每个 M 必须关联一个 P 才能执行 G,M 的数量通常由系统自动调整。:逻辑处理器,持有执行 Goroutine 所需的资源,包括本地运行队列(LRQ)、G 上下文缓存等。 阅读详情

相关推荐

Android-S无线模块前瞻

WiFi - mainlane非强制 S 采用反射的方法直接加载wifiservice,直接hook在systemserver上,无编译依赖。gms可以动态替换wifi服务。 R 采用直接启动service的方式,wifiservice仍然map在systemserver上。 WiFi - 11ad接口支持 11ad被wifi联盟商业化为WIGIG,提供短距的60GHZ载波,理论速度为当前5G 11ac的6倍。市面上基本没有手机支持,需要独立的芯片支撑此功能。 传输距离为3m以内,外界d.

头铁的伦的博客 1044

golang GMP模型

gmp模型能够在高并发场景下高效地管理和执行goroutine,最大化利用系统资源,同时保证系统的公平性和负载均衡。如果您觉得有帮助,请关注我,另公众号【小张的编程世界】,如有任何错误或建议,欢迎指出。

小张编程旅途的博客 1538

别再对着数据手册画图了!用HFSS 2023 R2快速复现ESP-01的PCB天线(附模型文件)

本文详细介绍了使用HFSS 2023 R2逆向复现ESP-01 PCB天线的五个关键步骤,包括逆向工程准备、高效建模技巧、材料与边界设置、激励与求解配置以及结果分析与设计验证。通过参数化建模和智能边界检测,大幅提升仿真效率,适用于WIFI和蓝牙设备的射频设计优化。

weixin_30832143的博客 76

go语言GMP模式介绍以及协程案例展示

Go语言的调度模型被称为GMP,这是一个高效且复杂的调度系统,用于在可用的物理线程上调度goroutines(Go的轻量级线程)。GMP模型由三个主要组件构成:Goroutine、M(机器)和P(处理器)。:管道在内部实现了必要的同步机制,因此在多个goroutines访问时是安全的。关闭管道后,不能再向管道发送数据,但仍可以接收管道中已存在的数据。:无缓冲管道在发送或接收时会阻塞,直到另一端准备好。尝试向已关闭的管道发送数据会引发panic。循环从管道接收数据,直到管道被关闭。

qq_49195366的博客 2973

UCIe相关开源项目记录

目前找到的和UCIe协议有关的5个开源项目,如果也有正在做的朋友,欢迎私信交流

m0_64102565的博客 823

PostgreSQL 在 Windows操作系统 无管理员权限情况下的部署详解

但是因为使用默认5432端口,我们修改了端口,所以连接报错,需要修改local连接信息,右键-属性修改相应信息即可,方式同上。(也别太任意吧,个人习惯工具放在不包含中文及特殊字符的目录,另外目录名尽量简短,避免不必要的麻烦)图中我解压到了自己的tools\postgresql18。1)如果想修改默认的5432端口,找到初始化目录中的(上面的tools\postgresql18\。此处替换成你的实际目录\tools\postgresql18\pgsql\pgAdmin 4\runtime。

dong_zhi2328的博客 1117

go GMP调度原理

gmp

weixin_41565755的博客 5792

深入浅出:UE4类型系统完整构建过程详解

UE4的类型系统是一个复杂但设计精巧的架构,它通过静态代码生成和运行时构建的完美配合,实现了强大的反射功能。理解这个过程不仅有助于解决开发中的问题,也能让你更好地利用UE4的各种高级特性。就像建造一栋大楼需要经过设计、备料、框架、装修、验收等多个阶段,UE4类型系统的构建也是一个多阶段、精细化的过程。每个阶段都有其特定的职责,共同协作完成从源代码到运行时对象的转换。

m0_45371381的博客 911

golang大杀器GMP模型

golang大杀器GMP模型学习

fengxiandada的博客 6104

别再花钱买商业软件了!这4款免费DIC工具,从散斑生成到应变分析全搞定

本文介绍了4款免费开源的DIC(数字图像相关技术)工具,涵盖从散斑生成到应变分析的全流程,帮助用户零成本构建专业级DIC工作流。这些工具在精度、算法透明度和生态整合方面具有优势,适用于材料力学和实验力学研究,可替代昂贵的商业软件。

weixin_42525369的博客 168

双RTX 3090实战:如何用vLLM低成本部署Qwen2.5-14B大模型(附避坑清单)

本文详细介绍了如何利用双RTX 3090显卡低成本部署Qwen2.5-14B大模型的完整指南,包括硬件选型、环境配置、vLLM优化及生产环境调优。重点解析了BF16混合精度在显存占用与推理质量上的平衡策略,并提供了实用的避坑清单和性能调优技巧,帮助开发者高效实现大模型部署。

pz890123456的博客 699

聊一聊Go中的GMP调度模型

调度模型的由来 早期操作系统是单进程的,那么执行进程只能是顺序执行的 顺序执行,效率比较低 当前进程的阻塞会带来CPU浪费,因为CPU没办法处理后面待处理的进程 后来出现了多进程操作系统,多进程处理下,会涉及到基于CPU调度器对进程分配时间片,宏观上三个进程在并发,实际还是顺序执行,这么看来,解决了进程阻塞带来对CPU浪费 但是这种方式会带来进程切换成本(进程切换涉及到拷贝复制)的浪费。 当进程数量越多,切换成本也就越浪费,CPU的一部分浪费在了切换上。 除此之外,进程和线程占用内存是很大的,进程虚拟

阳光大男孩!!!的博客 2183

SAP 物料主数据 Forecasting 视图,别把预测只当成一个数字字段

摘要(150字): SAP S/4HANA物料主数据的Forecasting视图常被忽视,但对依赖历史消耗规律的物料至关重要。该功能基于工厂维度的历史消耗数据,结合预测参数自动计算未来需求,动态调整重订货点和安全库存。预测模板(Forecast Profile)能标准化参数设置,而消耗数据(Consumption Values)的质量直接影响预测结果。预测值作为MRP输入,需注意保存操作才能生效。自动重订货点功能通过分析消耗波动和服务水平,实现库存策略的动态优化。Individual Forecast功能则

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1435

深入解析Golang GMP

本文深入解析了 Go 语言中的 GMP 调度模型,结合源码详细讲解了 Goroutine、M(Machine)和 P(Processor)之间的协作机制。首先,介绍了 Goroutine 的生命周期及状态管理,分析了状态切换的关键函数。随后,阐述了调度器通过主动、正常、抢占和被动调度,确保 Goroutine 在多核系统中高效并发执行。文中重点解析了 schedule()、findRunnable() 和 execute() 等核心函数的具体实现,展示了调度器如何通过本地队列、全局队列、工作窃取和网络轮询等

tatasix的博客 1972

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

GMP模型

本篇将会从起源、设计思想、设计策略、运行模式、现实场景多维度进行论述GMP。并在结尾补充了五个易混易错的要点

2302_80067378的博客 1893

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Golang-GMP调度模型

GM模型和GMP模型以及调度策略

wangye135的博客 1362

Golang源码探究——从Go程序的入口到GMP模型

在大多数的编程语言中,main函数都是用户程序的入口函数,go中也是如此。那么main.main是整个程序的入口吗, 肯定不是,因为go程序依赖于runtime,在程序的初始阶段需要初始化运行时,之后才会运行到用户的main函数,那么main.main是在哪里被调用的呢?接下来就从go程序的入口,再到go的GMP模型进行一个探究。

Peerless__的博客 1541

GO调度模型:GMP

GO的调度模型: 可以抽象出三个实体:M(machine)、P(processor)、G(goroutine)。 G:GO运行时对goroutine的描述,G中存放并发执行的代码入口地址、上下文、运行环境(关联的P和M)、运行栈等执行相关的信息。G的新建、休眠、恢复、停止都受到Go运行时的管理。 GO运行时的监控线程会监控G的调度,G不会长久地阻塞系统线程,运行时的调度器会自动切换到其他G上运行。G新建或恢复时会添加到运行队列,等待M取出并运行。 M:OS内核线程,是操作系...

qq_33690342的博客 4645

Golang GMP调度模型详解

文章目录前言1. Goroutine调度器的基本概念2. Goruntine调度策略2.1 work stealing机制2.2 hand off 机制 前言      线程数过多,意味着操作系统会不断地切换线程,频繁的上下文切换就成了性能瓶颈。      Golang的调度模型是GMP模型,它提供一种机制,可以在线程中自己实现调度,上下文切换更轻量,从而达到了线程数少,而并发数并不少的效果。而线程中调度的就

爱意时刻减淡,玫瑰不再浪漫 4555

一文解析golang中的协程与GMP模型

并发(并行)一直是在编程领域中一直受关注的一个核心主题。而go语言在诞生之初就带着「 高并发 」的明星光环,进入开发者的视野。而golang的高并发则是由goroutine与GMP调度模型实现的,因此探究goroutine与GMP的实现变得尤为重要。

幸平的博客 2405

Go并发GMP调度模型

Go和C++不同,Go的逃逸分析是在编译器完成的;go局部变量会进行逃逸分析。如果变量离开作用域后没有被引用,则优先分配到栈上,否则分配到堆上。那么如何判断是否发生了逃逸呢?go build -gcflags '-m -m -l' xxx.go.关于逃逸的可能情况:变量大小不确定,变量类型不确定,变量分配的内存超过用户栈最大值,暴露给了外部指针。如果变量内存占用较大时,优先放在堆上;如果函数外部没有引用,优先放在栈中;如果变量在函数外部存在引用;必定在堆中。协程泄漏是指协程创建之后没有得到释放。主要原因有:

2301_77174919的博客 2353

【Go】浅析Go语言并发GMP模型原理

【Go】浅析Go语言并发GMP模型原理

theaipower的博客 6822

golang gmp模型分析

就是告诉那些运行时间超过特定阈值(10ms)的G,该让一让了,怎么知道运行时间过长了呢,P里面有一个schedtick字段,每当调度执行一个新的G,并且不继承上个G的时间片时,就会把它自增1,而这个p.sysmontick中,schedwhen记录的是上一次调度的时间,监控线程如果检测到p.sysmontick.schedtick与p.schedtick不相等,说明这个P又发生了新的调度,就会同步这里的调度次数,并更新这个调度时间。实际上监控线程,调度器,GC等工作过程中都会按需执行netpoll。

xiyanxiyan10的专栏 1421

golang的GMP原理

在学习 golang 的 GMP 原理前,你可能需要了解一下关于进程、线程以及协程的知识,可以先瞅瞅这篇之前的这篇文章。 文章地址:golang的并发编程   1、G、M、P都代表什么意思 Processor处理器,其中包含了运行 goroutine 的资源,如果线程想运行 goroutine,那必须先获取 P,P 中还包含了可运行的 G 队列。   2、GMP模型 golang 中线程是运行 goroutine 的实体,调度器的作用是把可运行的 goroutine 分配到工作线程上。

Destiny_shine的博客 5392

Go 语言的 GMP 模型

本文深入探讨了Go语言中的高并发能力及其核心概念——协程。

BUG制造机的博客 1227
上一篇: 程序员如何做"好"需求判断?
下一篇: 职场进阶必会的通用方法论
TIGERB_Blog
博客等级 码龄10年 109粉丝 65原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值