Golang---GMP调度策略

Posted zpcoding

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang---GMP调度策略相关的知识,希望对你有一定的参考价值。

摘要:Go 能很好的支持并发模型,这也是 Go 如此火热的原因,那今天我们来学习 Go 的调度机制。

数据结构

G 结构体

  G 是 goroutine 的缩写,相当于操作系统中的进程控制块,在这里就是 goroutine 的控制结构,是对 goroutine 的抽象,下面是 G 的结构(只列出了部分与调度有关的):

技术图片
//用于保存上下文的 gobuf 结构体
type gobuf struct {
    sp   uintptr  //栈指针,上下文中的 sp 指针
    pc   uintptr  //程序计数器,上下文中的 pc 指针
    g    guintptr //指向当前 g  的指针
    ...
}
//用于表示一个等待链表上的 goroutine
type sudog struct {
    g *g  //阻塞列表上的 G

    next *sudog  //双向链表后指针
    prev *sudog  //双向链表前指针
    elem unsafe.Pointer //该 goroutine 的数据指针

    c        *hchan
    ...
}
基础结构

下面是 G 结构体:

技术图片
type g struct {
    stack       stack           // offset known to runtime/cgo

    m            *m            // current m; offset known to arm liblink
    sched        gobuf         //进程切换时,利用 sched 来保存上下文
    param        unsafe.Pointer // 用于传递参数,睡眠时其它 goroutine 设置 param, 唤醒时此 goroutine 可以获取到
    goid         int64          //goroutine 的 ID号

    lockedm        muintptr  //G 被锁定只能在这个 m 上运行
    gopc           uintptr   //创建这个goroutine 的go 表达式的 pc
    waiting        *sudog    //这个 g 当前正在阻塞的 sudog 结构体
}
G struct

M 结构体

  M 是 machine 的缩写,是对机器的抽象,每个 m 都是对应到一条操作系统的物理线程。M 必须关联了 P 才可以执行 Go 代码,但是当它处理阻塞或者系统调用中时,可以不需要关联 P。

技术图片
type m struct {
    g0      *g     // 带有调度栈的 goroutine(默认开启一个进程的时候会开启一个线程,又称主线程(g0))

    mstartfn      func()   //执行函数体的
    curg          *g       //当前运行的 goroutine
    p             puintptr //为了执行 Go 代码而获取的 p(如果不需要执行 Go 代码(syscall...),可为 nil)
    id            int64    //M 的 ID
    locks         int32
    park          note
    alllink       *m   //用于链接 allm(一个全局变量)
    schedlink     muintptr
    lockedg       guintptr  //某些情况下,goroutine 锁定到当前 m, 而不会到切换到其它 m 中去
    createstack   [32]uintptr // stack that created this thread.

    nextwaitm     muintptr    //期望获取锁的下一个 m

    syscall   libcall        //存储系统调用的参数
}
M struct

P 结构体

  P 是 Processor 的缩写。结构体 P 的加入是为了提高 Go 程序的并发度。一共有 GOMAXPROCS(一般为 CPU 的核数) 个 P, 所有的 P 被组织成一个数组(allp), 在 P 上实现了工作流窃取的调度器。

技术图片
type p struct {
    id          int32
    status      uint32 //P 状态 pidle/prunning/...
    link        puintptr
    schedtick   uint32     // 每次执行 goroutine 调度 +1
    syscalltick uint32     // 每次执行系统调用 +1
    sysmontick  sysmontick // last tick observed by sysmon
    m           muintptr   // 链接到它的 m (nil if idle)
    mcache      *mcache
    pcache      pageCache

    // Queue of runnable goroutines. Accessed without lock.
    // P 执行 Go 代码时,优先从自己这个局部队列中取,这时可以不用加锁,提高了并发度
    // 如果发现这个队列是空,则去其它 P 的队列中拿一半过来,实现工作流窃取的调度,这种情况需要给调度器加锁
    runqhead uint32  //本地 G 队列头
    runqtail uint32  //本地 G 队列尾
    runq     [256]guintptr  //本地 G 队列
    runnext guintptr  //下一个准备好运行的 goroutine

    sudogcache []*sudog
    sudogbuf   [128]*sudog
    ...
}
P struct

全局变量

 

调度策略

源码分析

总结

 

参考文献

 

以上是关于Golang---GMP调度策略的主要内容,如果未能解决你的问题,请参考以下文章

Linux 内核线程调度示例一 ② ( 获取指定调度策略的最大和最小优先级 | 代码示例 )

Linux 内核线程调度示例一 ③ ( 获取线程优先级 | 设置线程调度策略 | 代码示例 )

第三章 Goroutine调度策略(16)

MATLAB代码:基于多智能体系统一致性算法的电力系统分布式经济调度策略

Storm Nimbus默认的任务调度策略

Linux 内核进程优先级与调度策略 ② ( 获取调度策略对应的进程优先级函数 | sched_get_priority_max 函数 | sched_get_priority_min 函数 )