GMP模型
G(Goroutine)
Go协程,每个go关键字都会创建一个协程。
M(Machine)
操作系统线程,数量对应真实的CPU数()。
P(Processor)
处理器(go自定义概念,非CPU),线程和goroutine
的中间层,其数量可通过GOMAXPROCS
来设置,默认为核心数。
M必须拥有P才可以执行G中的代码,P含有一个包含多个G的队列,P可以调度G交由M执行。

特殊的M0
和G0
M0
是启动程序后的编号为0
的主线程,M0
负责执行初始化操作和启动第一个G
,其余和其他的M
一致。
G0
是每个M
第一个创建的goroutine,仅负责调度,不指向任何可执行的函数,与M
是1:1对应关系。在调度或系统调用时会使用G0
的栈空间 (system stack)

调度器
go func()
的调度流程

在调度器中,调度队列分为局部队列和全局队列。本地的运行队列是一个使用数组构成的环形链表,它最多可以存储256个待执行任务。当局部队列满时,会将G放入全局队列。
go 1.1
在GM模型中引入了处理器P,增加中间层;并在处理器P的基础上实现基于工作窃取(work-stealing)的调度器,当前本地的运行队列中不包含goroutine时,调用stealWork
从其它P的队列中随机获取一些goroutine。
go 1.2
引入基于协作的抢占式调度,go 1.14
引入基于信号的抢占式调度,但是目前只在垃圾回收扫描任务时触发。
基于协作的抢占式调度
编译器会在调用函数前插入morestack
;
运行时会在垃圾回收暂停程序、系统监控发现goruoutine运行超过10ms时发出抢占请求StackPreempt
;
当发生函数调用时,可能会执行编译器插入的morestack
->newstack
,检查goroutine的stackguard0
字段是否为StackPreempt
,若是的话,就会触发抢占让出当前线程;