调度模型
groutine能拥有强大的并发实现是通过GPM调度模型实现
G代表一个goroutine对象,每次go调用的时候,都会创建一个G对象
M代表一个线程,每次创建一个M的时候,都会有一个底层线程创建;所有的G任务,最终还是在M上执行
P代表一个处理器,每一个运行的M都必须绑定一个P,就像线程必须在么一个CPU核上执行一样
每一个P保存着本地G任务队列,也有一个全局G任务队列。
调用go时
创建一个G对象,加入到本地队列或者全局队列
如果还有空闲的P,则创建一个M,如果没找到则直接返回
M会启动一个底层线程,循环执行能找到的G任务
G任务的执行顺序是,先从本地队列找,本地没有则从全局队列找(一次性转移(全局G个数/P个数)个,再去其它P中找(一次性转移一半),
以上的G任务执行是按照队列顺序(也就是go调用的顺序)执行的。
抢占式调度
启动的时候,会专门创建一个线程sysmon,用来监控和管理
记录所有P的G任务计数schedtick,(schedtick会在每执行一个G任务后递增)
如果检查到 schedtick一直没有递增,说明这个P一直在执行同一个G任务,如果超过一定的时间(10ms),就在这个G任务的栈信息里面加一个标记(stackguard设置为StackPreempt然后相当于调用runtime.Gosched)
然后这个G任务在执行的时候,如果遇到非内联函数调用,就会检查一次这个标记,然后中断自己,把自己加到队列末尾,执行下一个G