runqget 中返回的 inheritTime 具体有什么作用呢?
来源:1-4 Go 程序是怎么跑起来的
xiaobaixiao
2021-05-17 17:34:07
runqget 会返回一个 inheritTime 值,看源码知道这个值跟 p.schedtick
有关,但是还是不理解为什么 runqget
需要返回这个值。跟时间片又有什么关系呢?
// If inheritTime is true, gp inherits the remaining time in the
// current time slice. Otherwise, it starts a new time slice.
// Never returns.
func runqget(_p_ *p) (gp *g, inheritTime bool) {
// If there's a runnext, it's the next G to run.
for {
next := _p_.runnext
if next == 0 {
break
}
if _p_.runnext.cas(next, 0) {
return next.ptr(), true
}
}
for {
h := atomic.LoadAcq(&_p_.runqhead) // load-acquire, synchronize with other consumers
t := _p_.runqtail
if t == h {
return nil, false
}
gp := _p_.runq[h%uint32(len(_p_.runq))].ptr()
if atomic.CasRel(&_p_.runqhead, h, h+1) { // cas-release, commits consume
return gp, false
}
}
}
1回答
结论: runnext与主协程共享时间片, 防止两者相互阻塞-唤醒导致其他协程饥饿.
我看了下源码, 如果调度的时候, 获取了runnext, 而不是其他队列的g, 那就继承时间片, 其他情况基本都是不继承.
在接下来的execute方法中, 可以看到. 如果不继承, 就schedtick++, 继承的话就不变.
这样的话, sysmon里根据schedtick来计算协程有没有运行太久. 如果schedtick变了, 那表示开启新的一轮计算. 没变的话, 表示复用一个时间片.
这样看起来像可以防止一个协程不停的启动/等待子协程, 然后子协程运行, 又复活父协程. (没有父子关系, 大概这个意思).
所以我去翻了下这个commit, 还比较难翻到. 基本我就是我说的这个意思.
https://go-review.googlesource.com/c/go/+/9289/
runtime: yield time slice to most recently readied G
这个是和runnext一起优化.
runnext可以加快上面说的运行模型
共享时间窗口, 又能避免因为不停的ping-pong双向唤醒导致其他协程饥饿
快来给我点赞吧.
by 助教听闻
相似问题