做者:林冠宏 / 指尖下的幽灵git
掘金:juejin.im/user/587f0d…github
GitHub : github.com/af913337456…并发
腾讯云专栏: cloud.tencent.com/developer/u…函数
正确地认识 G , M , P 三者的关系,可以对协程的调度机制有更深刻的理解! 本文将会完整介绍完 go 协程的调度机制,包含:性能
- 调度对象的主要组成
- 各对象的关系 与 分工
- gorutine 协程是如何被执行的
- 内核线程 sysmon 对 gorutine 的管理
- gorutine 协程中断挂起 与 恢复
- GOMAXPROCS 如何影响 go 的并发性能
BTW:本人技术书籍《区块链以太坊DApp开发实战》现已出版并可网购了,适合初中级区块链技术相关研发人员阅读。区块链
Golang 简称 Go,Go 的协程(goroutine)
和咱们常见的线程(Thread)
同样,拥有其调度器。优化
go 关键词
时候会建立的一个对象处理器
,又称上下文队列
队列中
提取 G,并执行GOMAXPROCS
(最大256),启动时固定的,通常不修改协程任务
交换全局队列
找gorutine
,它会加入到本地队列或者全局队列空队列
一个点
!
若是一个G任务执行时间太长,它就会一直占用 M 线程,因为队列的G任务是顺序执行的,其它G任务就会阻塞,如何避免该状况发生? --①spa
底层线程
,循环执行
能找到的 G 任务。这里的寻找的 G 从下面几方面找:
协程的切换时间片是10ms,也就是说 goroutine 最多执行10ms就会被 M 切换到下一个 G。这个过程,又被称为 中断,挂起
线程
原理:
go程序启动时会首先建立一个特殊的内核线程 sysmon
,用来监控和管理,其内部是一个循环:
记录全部 P 的 G 任务的计数 schedtick
,schedtick会在每执行一个G任务后递增
若是检查到 schedtick
一直没有递增,说明这个 P 一直在执行同一个 G 任务,若是超过10ms,就在这个G任务的栈信息里面加一个 tag 标记
而后这个 G 任务在执行的时候,若是遇到非内联函数调用,就会检查一次这个标记,而后中断本身,把本身加到队列末尾,执行下一个G
若是没有遇到非内联函数
调用(有时候正常的小函数会被优化成内联函数)的话,那就会一直执行这个G任务,直到它本身结束;若是是个死循环,而且 GOMAXPROCS=1 的话。那么一直只会只有一个 P 与一个 M,且队列中的其余 G 不会被执行!
例子,下面的这段代码,hello world
不会被输出
func main(){
runtime.GOMAXPROCS(1)
go func(){
fmt.Println("hello world")
// panic("hello world") // 强制观察输出
}()
go func(){
for {
// fmt.Println("aaa") // 非内联函数,这行注释打开,将致使 hello world 的输出
}
}()
select {}
}
复制代码
看完上面的内容,相信你已经知道,GOMAXPROCS
就是 go 中 runtime 包的一个函数。它设置了 P 的最多的个数。这也就直接致使了 M 最多的个数是多少,而 M 的个数就决定了各个 G 队列能同时被多少个 M 线程来进行调取执行!
故,咱们通常将 GOMAXPROCS 的个数设置为 CPU 的核数,且须要注意的是: