最近在学习goroutine,记点笔记...

golang中协程目前使用的是GMP模型

GMP描述

  • G: goroutine,可以理解为协程
  • M:线程,内核线程,goroutine跑在M之上的 ( M的数量在runtime/debug包的SetMaxThreads()决定。如果当前的M阻塞,就会新建一个新的线程。)
  • P:调度器,goroutine的队列 (P的数量由GOMAXPROCS环境变量,或者runtimeGOMAXPROCS()函数决定的。)

注意:M的数量和P的数量没有关系。如果当前的M阻塞,P的goroutine会运行在其他的M上,或者新建一个M。所以可能出现有很多个M,只有1个P的情况。

说明:

  1. 全局队列:要执行goroutines的队列
  2. P 本地队列:与全局队列一样,它包含要执行的goroutine;但是这个队列的最大容量是256。当本地队列已满时,会将gorouting的一半发送到全局队列;最后,G创建的Goroutine 将在同一个队列中排队,以确保本地关联
  3. P 列表:一旦确定GOMAXPROCS的值,所有P的列表都将被创建
  4. M 正在运行的线程:从P的本地队列获取任务,如果该队列为空,M将从全局队列获取goroutines到P的本地队列,或者从其他P的队列获取goroutines。G在M中运行,当任务完成时,它将继续拉一个新的G

Goroutine调度程序和OS调度程序使用M连接,每个M是一个物理OS线程,OS调度程序调度M运行在一个真正的CPU内核中。


go func() 执行流程:

  1. go func(){}创建一个新的goroutine
  2. G保存在P的本地队列,如果本地队列满了,保存在全局队列。
  3. G在M上运行,每个M绑定一个P。如果P的本地队列没有G,M会从其他P的本地队列,挥着G的全局队列,窃取G。
  4. 当M阻塞时,会将M从P解除。把G运行在其他空闲的M或者创建新的M。
  5. 当M恢复时,会尝试获得一个空闲的P。如果没有P空闲,M会休眠,G会放到全局队列。

oh yeah