Golang 协程调度器

GPM介绍:

G:goroutine协程

M:thread线程

P:processor处理器

废弃的调度器

M访问、放回G都必须访问全局G队列,M还有多个即多线程访问同一资源需要加锁进行保证互斥/同步,所以全局G队列是有互斥锁进行保护的

老的调度器是有以下缺点的:

1:创建、销毁、调度G时,都需要每个M获得锁,形成了激烈的锁竞争。

2:M转移G会造成延迟和额外的系统负载,例如当前的G创建了新的协程G’,这个G’加入到队列中可能被其他M执行了,因为G和G’是相关的,最好放在同一个M上执行。

3:系统调用(CPU在M之间切换)导致频繁的线程阻塞和取消阻塞操作,这些操作增加了系统开销

新的调度器

processor 包含了运行goroutine的资源,如果M想要运行G,M必须先获取P,P还包含了可运行的G队列

线程是运行goroutine的实体,调度器的功能就是把 可运行的goroutine分配到工作线程上

P的本地队列最大256个,新建 G’ 时优先放到P的本地队列,如果队列满了就把P本地队列的前一半的G加入到全局队列(打乱顺序),此时的 G’ 也会被加入到全局(有个疑问:已经把一半的G移动到全局队列 本地队列有位置了,G’为什么不放入本地队列)

调度策略:

1:复用线程:避免频繁的创建、销毁线程,而是对线程的复用

work stealing机制:当本线程没有可运行的G时,会先从全局队列获取 后从其他P队列偷G,而不是销毁线程

hand off 机制:当本线程因为G进行系统调用发生阻塞时,线程释放绑定的P,把P转移给其他空闲的线程,以便其他的M能获取P而运行

2:利用并行

3:抢占

4:全局G队列

 

创建G时,运行的G会尝试唤醒其他空闲M以便更多的PM组合来处理G,M去找空闲的P绑定,如果有空闲的P和M 那就组合,此时就是自旋线程了

自旋线程:

1:G唤醒了M,M绑定了P,并运行G0,但P本地队列没有G,M此时为自旋线程

2:当M把P的本地队列中的G都运行完了,全局队列里边也获取不到,其他P的本地队列也偷不到时,此时M也是自旋线程

一旦G0切换到其他的G 自旋状态就消失了

为什么会有自旋线程:

自旋的本质是在运行,线程在运行缺没有执行G,这就浪费了CPU。为啥不销毁现场来节约CPU资源:因为创建和销毁线程也耗CPU资源,系统希望的是当有新的goroutine到来时,立即有M运行它,如果销毁再新建就增加了时延,降低了效率

当然过多的自旋线程也会浪费CPU,所以系统中最多有GOMAXPROCS个自旋的线程,多余的没事做线程会让他们休眠。

空闲线程(休眠线程):

1:没有得到P绑定的M

 

 

当M正在运行G  假如G此时发生了阻塞系统调用,runtime会让M和P解绑,如果P的本地队列有G、全局队列有G并且有空闲的M,P都会立马唤醒1个M和它绑定,否则P会被加入到空闲的P列表等待M来获取

当M正在运行G  假如G此时发生了非阻塞系统调用,跟上边一样PM解绑,但是M会记住这个P,当这个G和M退出系统调用时会尝试获取有记忆的那个P,那个P被别的M占用(P改嫁了)就去P列表获取空闲P,还是获取不到的话,这个G会被记为可运行状态,并加入到全局队列

                         M因为没有P的绑定而变成休眠状态(长时间休眠等待GC回收销毁)

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值