调度相关:协作式调度和抢占式调度的实现和发生的时机是怎样的?

调度相关:协作式调度和抢占式调度的实现和发生的时机是怎样的?

协作式调度和抢占式调度的实现和发生的时机是怎样的? 还请老师帮忙解答一下。

正在回答

登陆购买课程后可参与讨论,去登陆

1回答

先从概念上理解:

  • 协作式调度依靠被调度方主动弃权;

  • 抢占式调度则依靠调度器强制将被调度方中断。


协作式调度发生的时机是在函数序言部分的扩栈检测指令,当检测到 stackGuard0 是一个特定值 stackPreempt 的时候,就会主动放弃 CPU 执行权。用编译命令 go tool compile -S 可以看到比较指令:


    http://img1.sycdn.imooc.com//climg/60a31b9f098272f314520786.jpg


接着就会跳到 morestack 执行。最终会执行到 goschedImpl,将 g 和 m 解绑,将 g 放到全局可运行队列里去。而 m 这时又会去找其他的 g 来执行。


http://img1.sycdn.imooc.com//climg/60a31bb309f1f20d14540756.jpg














主动调度弃权依赖在函数序言部分插入抢占检测指令,需要有函数调用。有些场景下,我们写出一个 for {i++} 这样的无限循环,就没法抢占了。这在 Go 1.14 之前会出现死机的事故。Go 1.14 实现了信号抢占,就可以解决这种场景。


预告一下,曹大在今年的 gopherchina 上会讲信号抢占这个 topic。


信号抢占的时机是:

  1. 当 goroutine 执行时间过长,超过 10ms,sysmon 会检测到,然后向这个 goroutine 所在的 m 发 SIGURG 信号。

  2. 2. GC 时会停止所有正在运行的 goroutine,这时也会向 m 发 SIGURG 信号。


m 收到信号后,会转去执行 sighandler 函数,sighandler 函数会做一些手脚(插入了一个抢占函数:asyncPreempt)就马上返回。返回之后,m 不会接着执行 goroutine 的指令了,而是去执行 asyncPreempt 去了,抢占就在这里发生:把当前的 goroutine 扔到全局可运行队列里,m 则去找其他的 goroutine 执行。
函数调用流程如下:


http://img1.sycdn.imooc.com//climg/60a31c0d09b9c30b06600478.jpg


关于这个话题,推荐一篇文章,写得非常好,值得看 10 遍:
https://golang.design/under-the-hood/zh-cn/part2runtime/ch06sched/preemption/

问题已解决,确定采纳
还有疑问,暂不采纳

恭喜解决一个难题,获得1积分~

来为老师/同学的回答评分吧

0 星
请稍等 ...
意见反馈 帮助中心 APP下载
官方微信

在线咨询

领取优惠

免费试听

领取大纲

扫描二维码,添加
你的专属老师