Golang 调度

开始

  • go程序启动,主机上定义的每一个虚拟内核都会为它分配一个逻辑处理器§,每个线程都会分配一个逻辑处理器
package main
import (
	"fmt"
	"runtime"
)
func main() {
    fmt.Println(runtime.NumCPU()) // MacbookPro 2019 4核心8线程,返回结果8
}
  • 会为每一个逻辑处理器(P)分配一个M(os线程)
  • 线程是OS来处理的,并且OS还负责把线程放置到一个core上去执行
  • 每个线程单独连到一个P上
  • Go调度器运行在内核之上的用户空间(user space),Go 调度器中有两种不同的运行队列
    • 全局运行队列(GRQ):负责管理没有分配到P的Goroutines
    • 本地运行队列(LRQ):每个P会分配一个LRQ去处理P的上下文要执行的Goroutines 。这些Goroutines会在绑定到P的M上进行上下文的切换

Goroutine三种状态

  • 等待中
  • 可执行
  • 执行中

Go程序中有4种类型的事件,允许调度器去做出调度决策

  • go关键字:一旦一个新的Goroutine创建好,调度器便有机会去做出调度决定
  • 垃圾回收
  • 系统调用:如果一个Goroutine进行系统调用导致了M的阻塞,调度器有时候会用一个新的Goroutine从M上替换下这个Goroutine。但是有时候会需要一个新的M去执行挂在P队列上的Goroutine
  • 同步处理:atomic、mutex或者是channel操作的调用导致了Goroutine的阻塞,调度器会切换一个新的Goroutine去执行

具体PMG模型过程可以看这个文章,更加详细Golang调度

  • go程序启动时会首先创建一个特殊的内核线程sysmon
  • 调度模型,go语言当前的实现是N:M。即一定数量的用户线程映射到一定数量的OS线程上,这里的用户线程在go中指的就是goroutine。go语言的调度模型需要弄清楚三个概念:M、P和G
    • M:OS线程
    • P:上下文可以理解为待执行goroutine队列
    • G:Goroutine
      在这里插入图片描述

触发PMG goroutine切换

  • goroutine主动调用runtime.goshed()方法让出M
  • goroutine调用runtime·park()方法,进入waiting状态
  • goroutine等待慢系统调用

为了使该P上挂着的其它G也能得到执行的机会,需要将这些goroutine转到另一个OS线程上去。具体的做法是:首先将该P设置为syscall状态,然后该线程进入系统调用阻塞等待。之前提到过的sysmom线程会定期扫描所有的P,发现一个P处于了syscall的状态,就将M和P分离(实际上只有当 Syscall 执行时间超出某个阈值时,才会将 M 与 P 分离)。RUNTIME会再分配一个M和这个P绑定,从而继续执行队列中的其它G。而当之前阻塞的M从系统调用中返回后,会将该goroutine放入全局等待队列中,自己则sleep去

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值