Go GMP模型原理分析

本文深入探讨了Go语言中的协程(Goroutine)、工作线程(Machine, M)和处理器(Processor, P)的概念及其交互。GMP模型确保了高效的任务调度,通过系统调用、线程池和工作量窃取策略平衡负载。此外,介绍了抢占式调度和sysmon监控线程的角色,以保证程序的响应性和性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GMP模型

G: goroutine 表示Go协程,每一个Go关键字都会创建一个协程.
M: machine 表示工作线程,有操作系统调度.
P: processor 表示处理器,包括运行Go代码的必要资源,以及调度goroutine的能力。

M必须持有P才可以执行代码,M会被系统调用阻塞。
P的个数在程序启动时决定,默认等于CPU数量。
M的个数通常稍大于P,因为除了运行Go代码,runtime包还有其他内置任务需要处理。在I/O密集型应用中,可以将GOMAXPROCS设置得大一些。


上图是协程从创建到销毁的完整过程,可以看到每一个P都会绑定一个M和一个本地的G队列。
针对一个某个特定的P,当本地的G队列已满或阻塞的协程G被唤醒,新创建的协程G会加入全局G队列中,P除了会调度本地队列,还会周期性地从全局G队列中摘取协程来调度。

调度策略

  1. 队列轮转

  2. 系统调用

    1. 前面提到M的数量略大于P,多出来的M在G发生系统调用时就可以发挥作用啦,Go提供了一个类似线程池的M的池子(这不就是线程池吗?),当有需要时,从池子中获取,用完就放回池子,不够的话就在创建(这难道还不是线程池吗?可恶)。
    2. 当G0发生系统调用时,M0会释放掉P0,从M池子中拿(或者新建)一个M1加载到P上,继续执行P本地队列中的G,M0因系统调用而阻塞,M1接替M0的工作,只要P不空闲,就能够保证充分利用CPU。
    3. G0结束系统调用,根据M0是否能够获取P,有两种不同的处理方法:
      1. 有空闲的P,M0获取P之后继续执行G0;
      2. 没有空闲的P,G0会放入全局的G队列,而M0将进入缓存池睡眠。
  3. 工作量窃取
    为了解决多个处理器P中G队列的均衡性,Go提供了工作量窃取策略,当P没有需要调度的协程时,会从全局队列中取,要是全局队列中也没有了,就从别的P队列中去偷,一次都取一半。

  4. 抢占式调度
    是指避免某个协程长时间执行,而阻碍其他协程被调度的机制。(有点类似时间片轮转)
    Go1.14之后引入了基于信号的抢占机制,解决了协程在陷入无限循环导致无法被抢占的问题。
    需要特别注意的是,Go runtime在启动程序的时候,会创建一个独立的M作为监控线程称为sysmon,不需要P就可以单独运行
    sysmon的职责
    1. 网络轮询器监控
    2. 系统调用syscall监控
    3. 垃圾回收

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hewesH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值