go trace 剖析 go1.14 异步抢占式调度

本文介绍了Go1.14引入的异步抢占式调度,通过分析go tool trace,对比Go1.13和Go1.14在处理纯计算任务时的调度表现,揭示了抢占调度如何提高系统吞吐率和公平性。通过实例展示了如何使用trace工具观察调度详情,强调了在只有一个处理器时,即使有并发,总执行时间不变。
摘要由CSDN通过智能技术生成

抢占调度

go 1.14 版本带来了一个非常重要的特性:异步抢占的调度模式。之前我们通过解释一个简单的协程调度原理(),并且实现协程调度例子都提到了一个点:协程是用户态实现的自我调度单元,每个协程都是君子才能维护和谐的调度秩序,如果出现了流氓(占着 cpu 不放的协程)你是无可奈何的。

go1.14 之前的版本所谓的抢占调度是怎么样的:

  1. 如果 sysmon 监控线程发现有个协程 A 执行之间太长了(或者 gc 场景,或者 stw 场景),那么会友好在这个 A 协程的某个字段设置一个抢占标记 ;
  2. 协程 A 在 call 一个函数的时候,会复用到扩容栈(morestack)的部分逻辑,检查到抢占标记之后,让出 cpu,切到调度主协程里;

这样 A 就算是被抢占了。我们注意到,A 调度权被抢占有个前提:A 必须主动 call 函数,这样才能有走到 morestack 的机会(旁白:能抢君子调度权利,抢占不了流氓的)。

举个栗子,然后看下 go1.13 和 go1.14 的分析对比:

特殊处理

  1. 为了研究方便,我们只用一个 P(处理器),这样就确保是单处理器的场景;
    1. 回忆下 golang 的 GMP 模型:调度单元 G,线程 M,队列 P,由于 P 只有一个,所以每时每刻有效执行的 M 只会有一个,也就是单处理器的场景(旁白:个人理解有所不同,有些人喜欢直接把 P 理解成处理器,我这里把 P 说成队列是从实现的角度来讲的);
  2. 打开 golang 调试大杀器 trace 工具(可以直观的观察调度的情况);
  3. 搞一个纯计算且耗时的函数 calcSum(不给任何机会);

下面创建一个名为 example.go 的文件,写入以下内容:

package main

import (
    "fmt"
    "os"
    "runtime"
    "runtime/trace"
    "sync"
)

func calcSum(w *sync.WaitGroup, idx int) {
   
    defer w.Done()
    var sum, n int64
    for ; n < 1000000000; n++ {
   
        sum += n
    }
    fmt.Println(idx, sum)
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值