Goroutine = Golang + Coroutine

GPM模型:对Goroutine分配、负载、调度

Untitled

Untitled

G:Goroutine

type g struct {
    stack       stack      // g自己的栈
    m            *m        // 隶属于哪个M
    sched        gobuf     // 保存了g的现场,goroutine切换时通过它来恢复
    atomicstatus uint32    // G的运行状态
    goid         int64
    schedlink    guintptr  // 下一个g, g链表
    preempt      bool      //抢占标记
    lockedm      muintptr  // 锁定的M,g中断恢复指定M执行
    gopc          uintptr  // 创建该goroutine的指令地址
    startpc       uintptr  // goroutine 函数的指令地址
}
状态 含义
_Gidle 0 刚刚被分配,还没有进行初始化。
_Grunnable 1 已经在运行队列中,还没有执行用户代码。
_Grunning 2 不在运行队列里中,已经可以执行用户代码,此时已经分配了 M 和 P。
_Gsyscall 3 正在执行系统调用,此时分配了 M。
_Gwaiting 4 在运行时被阻止,没有执行用户代码,也不在运行队列中,此时它正在某处阻塞等待中。Groutine wait的原因有哪些。参见代码
_Gmoribund_unused 5 尚未使用,但是在 gdb 中进行了硬编码。
_Gdead 6 尚未使用,这个状态可能是刚退出或是刚被初始化,此时它并没有执行用户代码,有可能有也有可能没有分配堆栈。
_Genqueue_unused 7 尚未使用。
_Gcopystack 8 正在复制堆栈,并没有执行用户代码,也不在运行队列中。

M:Machine

P:Processor

逻辑处理器,P关联了的本地可运行G的队列(也称为LRQ),最多可存放256个G。

type p struct {
    id          int32
    status      uint32     // P的状态
    link        puintptr   // 下一个P, P链表
    m           muintptr   // 拥有这个P的M
    mcache      *mcache  

    // P本地runnable状态的G队列,无锁访问
    runqhead uint32
    runqtail uint32
    runq     [256]guintptr
    
    runnext guintptr       // 一个比runq优先级更高的runnable G

    // 状态为dead的G链表,在获取G时会从这里面获取
    gFree struct {
        gList
        n int32
    }

    gcBgMarkWorker       guintptr // (atomic)
    gcw gcWork

}
状态 含义
_Pidle 0 刚刚被分配,还没有进行进行初始化。
_Prunning 1 当 M 与 P 绑定调用 acquirep 时,P 的状态会改变为 _Prunning。
_Psyscall 2 正在执行系统调用。
_Pgcstop 3 暂停运行,此时系统正在进行 GC,直至 GC 结束后才会转变到下一个状态阶段。
_Pdead 4 废弃,不再使用。

GMP调度流程大致如下: