结构——M, P, S
Go的调度器内部有三个重要的结构:M,P,S:
M: 内核OS线程
G: 一个goroutine,它有自己的栈,instruction pointer和其他信息(正在等待的channel等等),用于调度。
P: 代表调度的上下文,可以把它看做一个局部的调度器,使go代码在一个线程上跑,它是实现从N:1到N:M映射的关键。
(P的数量可以通过GOMAXPROCS()来设置,它其实也就代表了真正的并发度,即有多少个goroutine可以同时运行。)
灰色的G在等待执行。。。
线程阻塞——投奔其他线程
图中看到,当一个OS线程M0陷入阻塞时,P转而在OS线程M1上运行。调度器保证有足够的线程来运行所以的context P。
当MO返回时,它必须尝试取得一个context P来运行goroutine,一般情况下,它会从其他的OS线程那里steal偷一个context过来,如果没有偷到的话,它就把goroutine放在一个global runqueue里,然后自己就去睡大觉了(放入线程池里)。Contexts们也会周期性的检查global runqueue。
分配不均——steal work
- global runqueue——P 会周期性检查global runqueue;
- 其他的P——偷一半去执行;