【Go并发编程】Goroutine 调度器揭秘:从 GMP 模子到 Work Stealing 算法 ...

打印 上一主题 下一主题

主题 883|帖子 883|积分 2649

每天一篇Go语言干货,从焦点到百万并发实战,快来关注邪术小匠,一起探索Go语言的无限可能!
  在 Go 语言中,Goroutine 是一种轻量级的并发实行单元,它使得并发编程变得简朴高效。而 Goroutine 的高效调度机制是 Go 语言在并发处理上的一大亮点。本文将深入剖析 Go 语言的 Goroutine 调度器,从 GMP 模子到 Work Stealing 算法,带你一探究竟。
一、Goroutine 调度器的配景

Go 语言的并发模子基于 Goroutine,它是一种轻量级的线程,由 Go 运行时(runtime)主动管理。Goroutine 的调度机制决定了多个 Goroutine 怎样高效地映射到操作体系线程上实行。与传统线程(Thread)相比具有以下上风:

  • 内存占用仅2KB(线程默认1MB)
  • 上下文切换成本仅0.2μs(线程约1μs)
  • 创建速度达到微秒级(线程需毫秒级)
但是Goroutine本质上是用户态线程,需要依赖GMP调度器将其映射到操作体系线程(M)实行。
二、GMP 模子:Goroutine 调度的焦点

Goroutine 的调度基于 GMP 模子,即 Goroutine(G)、Machine(M)和 P(Processor)的组合。这个模子实现了从 N:1(用户态线程到内核态线程)到 N:M(用户态线程到内核态线程的灵活映射)的调度。
1. Goroutine(G)

Goroutine 是用户界说的协程,它代表了并发实行的任务。创建 Goroutine 的底层方法是newproc 函数,它会将 Goroutine 放入 P 的当地队列中。假如当地队列已满,则放入全局队列中。
  1. go func() {
  2.     // 任务代码
  3. }()
复制代码
2. Machine(M)

Machine 代表操作体系线程,是 Go 运行时与操作体系交互的接口。Go 运行时会根据需要创建和烧毁 M,以适应不同的并发场景。
3. Processor(P)

Processor 是 Go 运行时中的调度上下文,它负责管理 Goroutine 的调度。每个 P 有本身的当地队列,用于存储待实行的 Goroutine。
4.GMP模子示意图


通过该示意图可以了解到完整的GMP模子关系。
全局队列:当地队列(Processor调度器管理)满了的环境下,将会把新创建的Goroutine加入到全局队列中排队等待实行。
当地队列:存放即将实行的Goroutine,每个processor中的goroutine将并行实行。
Goroutine(G):图中的每个圆形图标G就是代表一个Groutine。
Processor(P):管理当前调度器内的当地队列,并负责管理 Goroutine 的调度,用于存储待实行的 Goroutine。processor和groutine是N:M的关系。
内核线程(M):每个M代表了一个内核线程,操作体系调度器负责把内核线程分配到CPU的核上实行。
三、调度器的工作流程

Go 调度器的焦点任务是从队列中获取可实行的 Goroutine,并将其分配给可用的 M 实行。
1. 当地队列

每个 P 都有一个当地队列,调度器会优先从当地队列中获取 Goroutine 实行。假如当地队列为空,则会尝试从全局队列获取。
2. 全局队列

全局队列是所有 P 共享的队列,用于存储未被分配的 Goroutine。当当地队列为空时,调度器会尝试从全局队列中获取 Goroutine。
3. Work Stealing(工作窃取)

假如当地队列和全局队列都为空,调度器会接纳 Work Stealing 算法,从其他 P 的当地队列中“偷取” Goroutine。这种策略可以实现线程之间的负载均衡。
  1. func runqsteal(pp, p2 *p, stealRunNextG bool) *g {
  2.     t := pp.runqtail
  3.     n := runqgrab(p2, &pp.runq, t, stealRunNextG)
  4.     if n == 0 {
  5.         return nil
  6.     }
  7.     n--
  8.     gp := pp.runq[(t+n)%uint32(len(pp.runq))].ptr()
  9.     if n == 0 {
  10.         return gp
  11.     }
  12.     h := atomic.LoadAcq(&pp.runqhead)
  13.     if t-h+n >= uint32(len(pp.runq)) {
  14.         throw("runqsteal: runq overflow")
  15.     }
  16.     atomic.StoreRel(&pp.runqtail, t+n)
  17.     return gp
  18. }
复制代码
四、抢占式调度

Go 调度器接纳抢占式调度策略,以防止某个 Goroutine 占用过多 CPU 资源。在 Go 1.14 之后,调度器在任何安全点都可以进行抢占。
  1. func findRunnable() (gp *g, inheritTime, tryWakeP bool) {
  2.     mp := getg().mtop
  3.     pp := mp.p.ptr()
  4.     // 每61次调度周期就检查一次全局G队列
  5.     if pp.schedtick%61 == 0 && sched.runqsize > 0 {
  6.         lock(&sched.lock)
  7.         gp := globrunqget(pp, 1)
  8.         unlock(&sched.lock)
  9.         if gp != nil {
  10.             return gp, false, false
  11.         }
  12.     }
  13.     // 本地队列
  14.     if gp, inheritTime := runqget(pp); gp != nil {
  15.         return gp, inheritTime, false
  16.     }
  17.     // 全局队列
  18.     if sched.runqsize != 0 {
  19.         lock(&sched.lock)
  20.         gp := globrunqget(pp, 0)
  21.         unlock(&sched.lock)
  22.         if gp != nil {
  23.             return gp, false, false
  24.         }
  25.     }
  26.     // 工作窃取
  27.     if mp.spinning || 2*sched.nmspinning.Load() < gomaxprocs-sched.npidle.Load() {
  28.         if !mp.spinning {
  29.             mp.becomeSpinning()
  30.         }
  31.         gp, inheritTime, _, _, _ := stealWork(now)
  32.         if gp != nil {
  33.             return gp, inheritTime, false
  34.         }
  35.     }
  36.     return nil, false, false
  37. }
复制代码
五、协作式调度

除了抢占式调度,Go 还支持协作式调度。Goroutine 可以通过调用runtime.Gosched() 函数主动让出 CPU 的实行权。
  1. func main() {
  2.     go func() {
  3.         for i := 0; i < 10; i++ {
  4.             fmt.Println("Goroutine 1")
  5.             runtime.Gosched()
  6.         }
  7.     }()
  8.     for i := 0; i < 10; i++ {
  9.         fmt.Println("Goroutine 2")
  10.     }
  11. }
复制代码
六、总结

Go 语言的 Goroutine 调度机制通过 GMP 模子和 Work Stealing 算法实现了高效的并发实行。抢占式调度和协作式调度策略确保了 Goroutine 的公平实行,而 Work Stealing 算法则进一步提高了多核处理器上的负载均衡。通过这些机制,Go 运行时可以或许高效地利用体系资源,实现高性能的并发编程。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

惊雷无声

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表