线程模型

golang线程模型的3个核心元

## M
matchine的缩写,一个M代表一个内核线程

## P
processor的缩写,一个P代表执行一个go代码片段所必须的资源(工作环境)

## G
goroutine的缩写,一个G是go的代码片段

M

M代表一个内核线程。大多数的情况下,创建一个M,是因为没有足够的M来关联P并运行其中的G。在运行时系统执行系统监控或者垃圾回收的时候也会创建M

// src/runtime/runtime2.go
type m struct {
	g0      *g     // goroutine with scheduling stack
	morebuf gobuf  // gobuf arg to morestack
	divmod  uint32 // div/mod denominator for arm - known to liblink

	// Fields not known to debuggers.
	procid        uint64       // for debuggers, but offset not hard-coded
	gsignal       *g           // signal-handling g
	goSigStack    gsignalStack // Go-allocated signal handling stack
	sigmask       sigset       // storage for saved signal mask
	tls           [6]uintptr   // thread-local storage (for x86 extern register)
	mstartfn      func()
	curg          *g       // current running goroutine
	caughtsig     guintptr // goroutine running during fatal signal
	p             puintptr // attached p for executing go code (nil if not executing go code)
	nextp         puintptr
	oldp          puintptr // the p that was attached before executing a syscall
	spinning      bool // m is out of work and is actively looking for work
	lockedg       guintptr
}

P

P是G能够在M上运行的关键,P会适时的与不同M进行关联,让可运行的G及时获得M。

P的数量决定着系统中G的队列的数量,改变P的数量有两种方式:

P的数量上限是:256。确定P的最大数量后,运行时系统根据这个数值重新调整全局P列表。

运行时系统也包含一个空闲P的列表,当P的G队列为空的时候才会进入空闲队列

P本身是由状态的。

P出了有可运行的G的列表,还有自由G列表。自由G列表主要是用来存放已经运行完成的G。这个队列长度超过预期值,则会将里面的部分G转移到调度器的自由G队列。当用go原语启动一个goroutine的时候,会先从自由G列表中获取一个G,如果获取不到,会新创建G。(系统发现P的自由G太少的时候或者为空,会尝试从调度器的自由G搬运过来一些)。

G

运行时系统包含一个G的列表,新创建的G会第一时间添加到这里。存放当前运行时系统的全部G指针。无论G是否是新的,运行时系统都会对其进行一次初始化,关联go函数,设置G状态等。G初始化完成后,会尝试存放在P.runnext字段(该字段存在P下一个要执行的g),如果该字段已经有数值,尝试放到P的运行G的队列末尾,如果队列也满了,则放到调度器的可运行G队列中。

G的状态 :

M, P, G容器列表

M-P-G容器列表