go调度机制简介
go中调度机制
G-M-P模型
G代表Goroutine即协程
M表示Machine即工作线程
P表示Processor即处理器
G-P-M关系
P中维护一个协程队列。除此之外还有一个全局的协程队列。
G会被放到P中的协程队列或者全局队列中
M必须要绑定一个P才能执行G
可以简单的理解为,P是一个资源池,用来存放G,M从P中取出G来执行。M可以理解成广义上的线程
P的数量一般是有GOMAXPROCS决定的,M的数量会略大于P的数量
调度过程:以创建新的G为例
创建G时,会先判断有没有空闲的P,有的话创建M绑定P,将新的G放到该P队列中。
如果没有空闲P,判断当前P队列是否满了,没满的话就放到P队列中,如果满了,会将P中的一半连同新的G一起放到全局队列中。
M周期性的从P中取出G来执行,并且定期从全局队列中取出G来执行,防止全局队列中的G被饿死。
当M因为当前G陷入系统调用或者阻塞时,M会记住当前的P,然后解绑P,P如果没有G了,就会进入空闲状态,
如果有G会从M缓存池取空闲的M或者创建一个M
来继续执行队列中的G。当M结束系统调用时,先找之前的P,没有找到会找到一个空闲的P,找到了就绑定P,将G放到P队列中,
继续执行
如果找不到的话,就将G放到全局队列中,陷入睡眠状态。
当P中的队列中不存在G,会从全局队列中取G来执行。如果全局队列也没有G。就从其他P中偷取,每次偷一半
每个M都有一个G0,用来在调度或者系统调用的时候使用其栈空间。
M0除了负责初始化操作和启动第一个G之后跟其他的M一样。
G执行完成后会切换为G0,然后G0负责调度协程切换
上面的文字可能比较抽象,我尽量用简单的场景来介绍。
以银行为例。把每个客户当作G,每个窗口当作P,每个工作人员当作M
每个窗口都有自己的客户队列,除此之外,还有一个全局的客户队列。
工作人员并不能直接处理客户请求,需要绑定一个窗口,即M需要绑定P才能从M中取出并运行G
这就是G-M-P的关系
当客户进来了,新的G到来。先判断下有没有空闲的窗口P?
有,那就去空闲的窗口P,然后窗口P找到一个工作人员M来执行客户请求。
没有,那么判断当前窗口P排队的人是不是满了,没满。那就在当前窗口P排队。
如果满了,那么就叫上当前窗口一半的客户一起到大厅(全局队列)排队。
工作人员M会从窗口P中排队的人找客户G。并且会定期从大厅的全局队列叫上客户G来运行。
防止大厅的客户等的不耐烦走了,即防止G饿死。
工作人员M处理客户G的时候可能需要去其他地方找资料,即阻塞。那么就会记住当前窗口P。然后解绑当前窗口P。等资料找到了即G工作执行完成,优先找到之前绑定的窗口。如果窗口被其他工作人员M占用了,那就看下有没有空闲的窗口P,没有的话就去休息,即陷入睡眠状态,把之前没执行完成的客户G放到全局队列里。
窗口P发现没有客户G排队了,那么就会进入空闲状态。就会去大厅找客户G,大厅里也没有客户G,那就去其他窗口偷客户G,每次偷一半
如果有客户,那么就创建或找一个工作人员M来执行G
个人理解,如有错误,欢迎友好交流。