M指的是Machine,一个M直接关联了一个内核线程(worker thread)。
    P指的是”processor”,代表了M所需的上下文环境,也是处理用户级代码逻辑的处理器。
    G指的是Goroutine,其实本质上也是一种轻量级的线程

    M - worker thread, or machine.
    P - processor, a resource that is required to execute Go code.
    M must have an associated P to execute Go code[…].
    G - goroutine.

    G:代表一个 goroutine,每个 goroutine 都有自己独立的栈存放当前的运行内存及状态。可以把一个G当做一个任务。

    M: 代表内核线程(Pthread),它本身就与一个内核线程进行绑定,goroutine 运行在M上。

    P:代表一个处理器,可以认为一个“有运行任务”的P占了一个CPU线程的资源,且只要处于调度的时候就有P。

    M关联了一个内核线程,通过调度器P(上下文)的调度,可以连接1个或者多个G,相当于把一个内核线程切分成了了N个用户线程;
    M和P是一对一关系(但是实际调度中关系多变),通过P调度N个G(P和G是一对多关系),实现内核线程和G的多对多关系(M:N),通过这个方式,一个内核线程就可以起N个Goroutine,同样硬件配置的机器可用的用户线程就成几何级增长,并发性大幅提高image.png
    image.png
    1、我们通过 go func()来创建一个goroutine

    2、有两个存储G的队列,一个是局部调度器P的本地队列、一个是全局G队列。新创建的G会先保存在P的本地队列中,如果P的本地队列已经满了就会保存在全局的队列中

    3、G只能运行在M中,一个M必须持有一个P,M与P是1:1的关系。M会从P的本地队列弹出一个可执行状态的G来执行,如果P的本地队列为空,就会向其他的MP组合偷取一个可执行的G来执行

    4、一个M调度G执行的过程是一个循环机制

    5、当M执行某一个G时候如果发生了syscall或则其余阻塞操作,M会阻塞,如果当前有一些G在执行,runtime会把这个线程M从P中摘除(detach),然后再创建一个新的操作系统的线程(如果有空闲的线程可用就复用空闲线程)来服务于这个P

    6、当M系统调用结束时候,这个G会尝试获取一个空闲的P执行,并放入到这个P的本地队列。如果获取不到P,那么这个线程M变成休眠状态, 加入到空闲线程中,然后这个G会被放入全局队列中