协程调度模型:协程是轻量的线程,在线程基础上,没有上下文切换,无锁,效率高;协程有自己的调度模型,即gmp模型;
g:代表go关键字开的协程;
p:逻辑处理器,绑定一个m处理g;包括上下文信息,维护了自己需要调度的g的局部队列,一个go新建优先放入局部队列,局部队列小于256,超过拿一半放入全局队列;由GOMAXPROCS设置个数;
m:对应一个系统线程,内核态;m绑定p处理局部go队列,如果没有从全局队列拿一批,若全局队列没有,从其他p局部队列偷一半(即m空闲时,会从其他队列偷取,不会直接销毁线程)
用go关键字新建一个goroutine发生的调度过程:
1.把goroutine放入本地队列,本地满放入全局队列;
2.m绑定p执行g,优先本地队列获取,本地没有去全局队列获取,全局队列没有,去其他p的局部队列获取;
3.m执行g,若g因系统调用或其他原因阻塞,将m和p接触绑定,把p转到其他空闲(这个空闲的m可能来自于m之前阻塞挂起一段时间后返回的m)或新建(这个新建会导致m的数量大于p的数量,即,m和p数量上不上相等的)的m上执行;
4.执行完销毁g,从p的局部(本地)队列循环获取g执行;
notice:
1.m的数量限制最大10000,由SetMaxThreads设置,有m阻塞会新建一个,有m空闲会回收或睡眠;
2.go运行时间过长,会有sysmon监控,它会对schedtick增长计数,即每执行完一个g后递增(栈增长),如果长时间没有增长,说明执行同一个任务g,当超过10ms会在栈信息添加标记,之后执行在遇到非内联函数调用,会中断自己,加入队列尾;当没有遇到非内联函数时,会一直执行;
go func(){
for{}
}
出现这种情况,同时p=1,会夯住;在1.14版本后采用了基于信号的抢占式调度进行了优化;
【Go gmp调度模型】3.g的中断恢复,中断时把寄存器中的栈信息保存到自己的g对象里,再次执行把栈信息复制到寄存器,继续上次后执行;