goroutine调度器有三个主要概念
当M想要执行Go代码时,必须有一个相应的P才能执行。
struct P
{
Lock;
G *gfree; // freelist, moved from sched
G *ghead; // runnable, moved from sched
G *gtail;
MCache *mcache; // moved from M
FixAlloc *stackalloc; // moved from M
uint64 ncgocall;
GCStats gcstats;
// etc
...
};
P *allp; // [GOMAXPROCS]
P的数量由GOMAXPROCS来控制,所有的P都存储在一个实现了work-stealin调度算法的数组里。每当GOMAXPROCS变化时,都会发生stop/start the world,然后调整P的数量。
P *idlep; // lock-free list
同时还有一个存储空闲P的idlep列表,每次M想要执行Go代码时,会从idlep列表里面取一个P,用完了再放回去。
当创建一个新的G或者一个已经存在的G将要运行时,都会把这个G放到当前的P的goroutine队列里面。每次P都从这个队列里拿一个G出来运行,如果拿不到,就随机选择另一个P2,把P2的goroutine队列偷一半过来。