脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|shell|

服务器之家 - 脚本之家 - Golang - 浅析go语言如何实现协程的抢占式调度的

浅析go语言如何实现协程的抢占式调度的

2024-04-23 16:18shark_chili Golang

go语言通过GMP模型实现协程并发,为了避免单协程持续持有线程导致线程队列中的其他协程饥饿问题,设计者提出了一个抢占式调度机制,本文会基于一个简单的代码示例对抢占式调度过程进行深入讲解剖析

详解协程抢占式调度

函数调用间进行抢占式调度

假设我们现在有这样一个协程,它会进行函数嵌套调用,代码如下所示:

func foo1() {
 fmt.Println("foo1调用foo2")
 foo2()
}

func foo2() {
 fmt.Println("foo2调用foo3")
 foo3()
}

func foo3() {
 fmt.Println("foo3")
}

func main() {
 //设置WaitGroup等待协程运行结束
 var wg sync.WaitGroup
 wg.Add(1)
 //通过协程调用foo1
 go func() {
  defer wg.Done()
  foo1()
 }()
 //等待协程运行结束
 wg.Wait()
}

我们给出运行结果:

foo1调用foo2
foo2调用foo3
foo3

基于这段代码示例,我们通过这段指令获取plan9汇编码:

go build -gcflags -S main.go

可以看到在foo1插入runtime.morestack_noctxt方法,该方法是用于检查当前协程是否有足够的堆栈空间以保证函数的正常调用,基于这一点,go就会在进行这部检查时顺带检查协程的执行时长,一旦超过10ms该方法就会将协程设置为标记可被抢占:

 0x0061 00097 (F:\github\test\main.go:8) CALL    runtime.morestack_noctxt(SB)

如下图,我们的调用的函数都会被插入一个morestack通过这个标记判断当前协程执行耗时,一旦发现超过10ms则会直接通过抢占式调度的方法g0协程直接调用schedule方法获取另外的协程进行调用:

浅析go语言如何实现协程的抢占式调度的

这一点我们可以在asm_amd64.s看到morestacknewstack的代码,而newstack就是实现抢占式调度的核心:

TEXT runtime·morestack(SB),NOSPLIT,$0-0
 // Cannot grow scheduler stack (m->g0).
 get_tls(CX)
 MOVQ g(CX), BX
 MOVQ g_m(BX), BX
 MOVQ m_g0(BX), SI
 CMPQ g(CX), SI
 JNE 3(PC)
 CALL runtime·badmorestackg0(SB)
 CALL runtime·abort(SB)

    //......
    //函数调用前会调用newstack进行抢占式的检查
 CALL runtime·newstack(SB)
 CALL runtime·abort(SB) // crash if newstack returns
 RET

上述的newstack方法在stack.go中,如果当前协程可被抢占则会调用gopreempt_m回到g0调用schedule方法从协程队列中拿到新的协程执行任务:

func newstack() {
 preempt := stackguard0 == stackPreempt


 //如果preempt 为true,则直接当前协程被标记为抢占直接调用gopreempt_m让出线程执行权
 if preempt {
  if gp == thisg.m.g0 {
   throw("runtime: preempt g0")
  }
  //......

  // Act like goroutine called runtime.Gosched.
  gopreempt_m(gp) // never return
 }
}

基于系统调用发起信号的抢占式调度

假设我们的协程没有进行额外的函数调用,是否就意味着当前协程的线程不能被抢占呢?很明显不是这样:

网络传输过程中需要发送某些紧急消息希望通过已有连接迅速将消息通知给对端时,就会产生SIGURG信号,go语言就会在收到此信号时触发抢占式调度。

浅析go语言如何实现协程的抢占式调度的

进行GC工作时像目标线程发送信号由此实现抢占式调度。

对于第一点我们可以在signal_unix.gosighandler方法得以印证,可以看到它会判断sig 是否为_SIGURG若是则调用doSigPreempt进行抢占式调度

func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
 
 //如果传入的信号为_SIGURG则调用doSigPreempt回到schedule实现抢占式调度
 if sig == sigPreempt && debug.asyncpreemptoff == 0 && !delayedSignal {
  // Might be a preemption signal.
  doSigPreempt(gp, c)
  
 }
 //......
}

doSigPreempt会通过调用asyncPreempt最终执行到preempt.goasyncPreempt2调用到和上文函数调用抢占式调度方法gopreempt_m回到schedule方法从而完成抢占式调度:

func doSigPreempt(gp *g, ctxt *sigctxt) {
 //......
 if wantAsyncPreempt(gp) {
  if ok, newpc := isAsyncSafePoint(gp, ctxt.sigpc(), ctxt.sigsp(), ctxt.siglr()); ok {
   // 调用asyncPreempt内部会得到一个和上文函数调用时抢占式调度的方法gopreempt_m的调用从而回到schedule方法
   ctxt.pushCall(abi.FuncPCABI0(asyncPreempt), newpc)
  }
 }

 //......
}

到此这篇关于浅析go语言如何实现协程的抢占式调度的的文章就介绍到这了,更多相关go协程抢占式调度内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://juejin.cn/post/7360595729524047912

延伸 · 阅读

精彩推荐
  • GolangGo代码检查的推荐工具及使用详解

    Go代码检查的推荐工具及使用详解

    这篇文章主要为大家介绍了Go代码检查的推荐工具及使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪...

    Luoyger8512022-07-27
  • GolangGo 内联优化让程序员爱不释手

    Go 内联优化让程序员爱不释手

    这篇文章主要介绍了Go 内联优化让程序员爱不释手,内联是在编译过程中自动进行的一类基本优化之一,文章围绕主题展开更多详细介绍,具有一定的参考...

    煎鱼eddycjy10422022-10-24
  • Golang浅谈golang并发操作变量安全的问题

    浅谈golang并发操作变量安全的问题

    这篇文章主要介绍了浅谈golang并发操作变量安全的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    思维的深度6572021-03-17
  • GolangGO中优雅编码与降低圈复杂度详析

    GO中优雅编码与降低圈复杂度详析

    Go语法简单易用,有其他编程经验的开发者,相信学习并快速上手Go语言的开发,多数觉得不困难吧,下面这篇文章主要给大家介绍了关于GO中优雅编码与降低圈复...

    阿兵云原生4942022-12-12
  • GolangGo语言里的结构体文法实例分析

    Go语言里的结构体文法实例分析

    这篇文章主要介绍了Go语言里的结构体文法,实例分析了结构体文法的概念及使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下 ...

    不是JS3702020-04-16
  • GolangGo Excelize API源码解析GetSheetFormatPr使用示例

    Go Excelize API源码解析GetSheetFormatPr使用示例

    这篇文章主要为大家介绍了Go Excelize API源码解析GetSheetFormatPr使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职...

    丘山子9122022-08-17
  • Golangjenkins构建go及java项目的方法

    jenkins构建go及java项目的方法

    这篇文章主要介绍了jenkins构建go及java项目,本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值了,需要的朋友...

    funnyZpC8272021-04-23
  • GolangGo Java算法之同构字符串示例详解

    Go Java算法之同构字符串示例详解

    这篇文章主要为大家介绍了Go Java算法之同构字符串示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪...

    黄丫丫4442022-08-16