手记

【九月打卡】第14天 go的协程-协程的抢占式调度

课程名称深入Go底层原理,重写Redis中间件实战


课程章节:5-7,5-8


课程讲师:Moody


课程内容:

   协程虽然可以被gopark和系统调用完成时这样的操作挂起,但是如果没有这两个操作,协程依然无法被挂起。这就需要一个机制来主动挂起协程。

    

通过go build -gcflags -S main.go 来看,系统在进行方法调用的时候,执行了runtime.morestack方法。

morestack方法本意是用来在方法调用的时候检查栈是否有足够的空间。

go在这个经常被调用的方法里面做了钩子,系统监控到goroutine运行超过10毫秒,就认为这个是个大协程,因此就会进行标记抢占。

※ 标记抢占

  • 系统监控协程运行超过10ms

  • 将g.stackguard0 设置为0xfffffade (抢占标志)

  • 执行morestack()时判断是否已经被标记抢占

  • 如果被抢占,直接回到schedule()

※ 基于信号抢占

如果方法并没有涉及到函数调用,那么就永远不会去检查morestack,那么也不会去检查标记位置,最后实现调度。

那么就可以实现一个基于信号的抢占。

  • 操作系统底层中有很多基于信号的通信方式

  • 线程可以注册对应信号的处理函数

  • 注册信号SIGURG 紧急信号的处理函数,该信号用的地方非常少,基本不会发生冲突

  • GC工作的时候,向目标线程发送信号,因为GC的时候很多工作都停了,适合做抢占

  • 线程收到信号的时候会触发调度

doSigPreempt方法会执行一个汇编方法,最终依然回调到mcall。

该流程主要是在GC里面下钩子,GC每过一段时间必然会进行垃圾回收,正是这个时刻,向线程发起调度信号,从而完成协程调度


课程收获:

这节课基本解释了协程的被动调用方式,明白了这个原理后,就知道为什么协程会比线程快很多,因为协程本身在切换的时候基本不耗费硬件的资源,完全是依赖线程本身的一些功能进行调度

0人推荐
随时随地看视频
慕课网APP