go的调度器
GMP调度模型
- G表示Goroutine;
- M表示一个操作系统的线程;
- P表示一个CPU处理器,通常P的数量等于CPU核数(GOMAXPROCS)。
如上图所示
- 全局队列的作用是负载均衡(Balance)。
- M需要和P绑定后不停的执行G的任务。
- P本地有个任务队列,可以无锁状态下执行高效操作。当本地队列为空时会尝试去全局队列去获取G,如果全局队列也为空,这个时候从其他有G的P哪里偷取一半G过来,放到自己的P本地队列。
- M和P并不一定是一一对应的,通常P数量是等于GOMAXPROCS,M的数量则由调度器的监控线程决定的。
我们再看下《Go 1.5 源码剖析 》中的示意图
+-------------------- sysmon ---------------//-------+
| |
| |
+---+ +---+-------+ +--------+ +---+---+
go func() ---> | G | ---> | P | local | <=== balance ===> | global | <--//--- | P | M |
+---+ +---+-------+ +--------+ +---+---+
| | |
| +---+ | |
+----> | M | <--- findrunnable ---+--- steal <--//--+
+---+
| 1. 语句go func() 创建G
| 2. 放入P本地队列或者平衡到全局队列
+--- execute <----- schedule 3. 唤醒或者新建M执行任务
| | 4. 进入调度循环 schedul
| | 5. 竭力获取待执行 G 任务并执行
+--> G.fn --> goexit ----+ 6. 清理现场,重新进入调度循环
M 通过修改寄存器,将执⾏栈指向 G ⾃带栈内存,并在此空间内分配堆栈帧,执⾏任务函数。
当需要中途切换时,只要将相关寄存器值保存回 G 空间即可维持状态,任何 M 都可据此恢复执⾏。
线程仅负责执⾏,不再持有状态,这是并发任务跨线程调度,实现多路复⽤的根本所在。
调试调度器
调试go调度器,我们有专门的小节来介绍GODEBUG追踪调度器