Go语言GMP模型

Goroutine

Go 语言基于GMP 模型实现用户态线程

  • G:表示goroutine,每个goroutine 都有自己的栈空间,定时器,初始化的栈空间在2k 左右,空间会随着需求增长。
  • M:抽象化代表内核线程,记录内核线程栈信息,当goroutine 调度到线程时,使用该goroutine 自己的栈信息。
  • P:代表调度器,负责调度goroutine,维护一个本地goroutine 队列,M 从P 上获得goroutine 并执行,同时还负责部分内存的管理。

GMP运作模型

  • 全局队列(Global Queue):存放等待运行的 G。
  • P 的本地队列:同全局队列类似,存放的也是等待运行的 G,存的数量有限,不超过 256 个。新建 G’时,G’优先加入到 P 的本地队列,如果队列满了,则会把本地队列中一半的 G 移动到全局队列。
  • P 列表:所有的 P 都在程序启动时创建,并保存在数组中,最多有 GOMAXPROCS(可配置) 个。
  • M:线程想运行任务就得获取 P,从 P 的本地队列获取 G,P 队列为空时,M 也会尝试从全局队列拿一批 G 放到 P 的本地队列,或从其他 P 的本地队列偷一半放到自己 P 的本地队列。M 运行 G,G 执行之后,M 会从 P 获取下一个 G,不断重复下去。

P和M的数量关系

  • P的数量由启动时环境变量 $GOMAXPROCS 或者是由 runtime 的方法 GOMAXPROCS() 决定
  • M的最大数量默认为10000(基本可忽略),也可以由runtime/debug 中的 SetMaxThreads 函数,设置 M 的最大数量

P和M的创建时机

在确定了 P 的最大数量 n 后,程序运行时系统会根据这个数量创建 n 个 P。

当没有足够的M来绑定对应的P的时候会创建,包括以下两个场景:

  • 当某个G系统调用阻塞,P将与当前M解绑,寻找一个休眠的M重新进行绑定,当休眠的M不够时,则会创建一个新的,之前的M处理完任务后因为没有P与之绑定则会进入休眠状态
  • 当在创建新的G时,会尝试去绑定空闲的P与M,如果这时休眠的M不足,也会创建新的M);
    综上,也可以得出结论,P与M的数量并没有直接关系

Goroutine 创建过程

获取或者创建新的Goroutine 结构体

从处理器的gFree(gFree是一个Goroutine缓存池列表)中查找空闲的Goroutine
如果不存在空闲的Goroutine,会通过runtime.malg 创建一个栈大小足够的新结构体

  • 将函数传入的参数移到Goroutine 的栈上
  • 更新Goroutine 调度相关的属性,更新状态为_Grunnable
  • 返回的Goroutine 会存储到全局变量allgs 中

将Goroutine 放到运行队列上

  • Goroutine 设置到处理器的runnext 作为下一个处理器执行的任务
  • 当处理器的本地运行队列已经没有剩余空间时,就会把本地队列中的一部分Goroutine 和待加入的Goroutine通过runtime.runqputslow 添加到调度器持有的全局运行队列上

调度器行为

  • 为了保证公平,当全局运行队列中有待执行的Goroutine 时,通过schedtick 保证有一定几率(1/61)会从全局的运行队列中查找对应的Goroutine
  • 从处理器本地的运行队列中查找待执行的Goroutine

如果前两种方法都没有找到Goroutine,会通过runtime.findrunnable 进行阻塞地查找Goroutine

  • 从本地运行队列、全局运行队列中查找
  • 从网络轮询器中查找是否有Goroutine 等待运行
  • 通过runtime.runqsteal 尝试从其他随机的处理器中窃取待运行的Goroutine
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值