做者:林冠宏 / 指尖下的幽灵git
GitHub : https://github.com/af913337456/并发
腾讯云专栏: https://cloud.tencent.com/developer/user/1148436/activities函数
正确地认识 G , M , P 三者的关系,可以对协程的调度机制有更深刻的理解! 本文将会完整介绍完 go 协程的调度机制,包含:性能
- 调度对象的主要组成
- 各对象的关系 与 分工
- gorutine 协程是如何被执行的
- 内核线程 sysmon 对 gorutine 的管理
- gorutine 协程中断挂起 与 恢复
- GOMAXPROCS 如何影响 go 的并发性能
Golang 简称 Go,Go 的协程(goroutine)
和咱们常见的线程(Thread)
同样,拥有其调度器。spa
go 关键词
时候会建立的一个对象处理器
,又称上下文队列
队列中
提取 G,并执行GOMAXPROCS
(最大256),启动时固定的,通常不修改协程任务
交换全局队列
找gorutine
,它会加入到本地队列或者全局队列空队列
一个点
!
若是一个G任务执行时间太长,它就会一直占用 M 线程,因为队列的G任务是顺序执行的,其它G任务就会阻塞,如何避免该状况发生? --①线程
底层线程
,循环执行
能找到的 G 任务。这里的寻找的 G 从下面几方面找:
协程的切换时间片是10ms,也就是说 goroutine 最多执行10ms就会被 M 切换到下一个 G。这个过程,又被称为 中断,挂起
code
原理:协程
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 的核数,且须要注意的是: