GPM调度

Golang 2022年05月18日 16:47 615  

什么是GPM

G:groutine,Go语言协程

P:processor,可以理解为Go的调度器

M:machine,直接关联一个os内核线程,执行通过调度器输送过来的G

  最初Go语言的调度模型中只有GM,待执行的G按顺序排好等待M的调用,每个M获取到G以后都要加锁,多个M分担多个G的执行,每次都要加锁解锁等待,影响程序并发性能。

  因此在Go1.1版本后引入了P,有个本地runq [256]guintptr将P关联到M,这样就不会总去全局队列中争抢G。
P 的本地可运行队列的长度为 256,它是一个循环队列,因此最多只能放下 256 个 goroutine。
还有当前执行的G,和存在可运行的队列256,所以总共可以有257个G运行及待运行。

执行原理

  main程序执行,并不是从main.mian开始的,因为不同平台下程序执行入口不同,再一系列检查下与初始化后,从runtime.main开始创建main goroutine,然后才会调用程序的main.main

Go语言协程对应的数据结构是runtime.g 工作线程对应的数据结构是runtime.m P对应全局变量的runtime.p,初始化中会根据GPMAXPROCS配置创建多少个p P还有一个全局runq 保存在全局变量sched(调度器),对应数据结构是runtime.schedt,这里记录着所有空闲的G和M等许多和调度相关的东西

全局变量g0就是主协程对应的g,协程栈是由主线程分配的 全局变量m0就是主线程对应的m,g0持有m0的指针,m0持有g0的指针,一开始m0执行的协程就是g0 全局变量allgs记录所有的g,allm记录所有的m,allp记录所有的p

如果再main执行过程中time.Sleep

协程执行time.Sleep时状态会从_Gruning变为_Gwaiting,并进入对应timer中等待,timer有个指定回调函数,在等待时间完成后调用这个回调函数

协程让出

无论用time.Sleep或者用channel低层都会调用到gopack来实现协程让出,都会goready把协程回复到runnable状态放回到runq中