GoExpertProgramming icon indicating copy to clipboard operation
GoExpertProgramming copied to clipboard

goroutine 调度疑问

Open Wang-Kai opened this issue 5 years ago • 5 comments

感谢开源出这么好的文章,这一定是我在 Github 发现的最有价值的仓库之一。

在协程调度一文 3.1 队列轮转 中,有提到如下观点:

上图中可见每个P维护着一个包含G的队列,不考虑G进入系统调用或IO操作的情况下,P周期性的将G调度到M中执行,执行一小段时间,将上下文保存下来,然后将G放到队列尾部,然后从队列中重新取出一个G进行调度。

文中的观点是 P 存在有类似时间片的调度机制,不管 G 是否执行完毕,会把它重新放入队列,再取出新的 G 执行。

但我在另外一篇文章中又读到

Once a runnable G is found, it is executed until it is blocked.

P 会一直执行 G,直到 G 处于阻塞状态。

求作者答疑,解决我的困惑。

Wang-Kai avatar Oct 03 '19 04:10 Wang-Kai

你过誉了:)

查看了部分源码,现在更倾向于Once a runnable G is found, it is executed until it is blocked.这个说法。

https://github.com/golang/go/blob/2197321db1dd997165c0091ba2bcb3b6be7633d0/src/runtime/proc.go#L2165-L2174

这里有协程执行的函数

// Schedules gp to run on the current M.
// If inheritTime is true, gp inherits the remaining time in the
// current time slice. Otherwise, it starts a new time slice.
// Never returns.
//
// Write barriers are allowed because this is called immediately after
// acquiring a P in several places.
//
//go:yeswritebarrierrec
func execute(gp *g, inheritTime bool) {

协程一旦被调度,没有主动弹出的操作。

协程这部分代码有点复杂,还没仔细看,这个章节也是不完整的。 先暂时搁置,待更详细的分析源码后再修改。

PS:如果你有兴趣,可以深入研究下:)

RainbowMango avatar Oct 09 '19 03:10 RainbowMango

貌似是在函数调用的时候,在栈上做了特殊处理。当发生函数调用,会检查某些条件(具体啥条件我也不知道),如果触发了该条件,则会调度另外一个G运行。

liyonglion avatar Apr 24 '20 08:04 liyonglion

貌似是在函数调用的时候,在栈上做了特殊处理。当发生函数调用,会检查某些条件(具体啥条件我也不知道),如果触发了该条件,则会调度另外一个G运行。

对的。正是因为依赖函数调用才能实现G的切换,所以如果函数没有发生函数调用(死循环)时,G就无法被抢占,Go在1.14版本实现的抢占式调度正是解决的这个问题。

RainbowMango avatar Apr 24 '20 08:04 RainbowMango

貌似是在函数调用的时候,在栈上做了特殊处理。当发生函数调用,会检查某些条件(具体啥条件我也不知道),如果触发了该条件,则会调度另外一个G运行。

对的。正是因为依赖函数调用才能实现G的切换,所以如果函数没有发生函数调用(死循环)时,G就无法被抢占,Go在1.14版本实现的抢占式调度正是解决的这个问题。

感谢 @RainbowMango 贡献如此好文,结合你的文章,再看源码如虎添翼

liyonglion avatar Apr 24 '20 08:04 liyonglion

哈哈,你这么说我很高兴,能帮助别人看源码也算没白费力。

RainbowMango avatar Apr 24 '20 08:04 RainbowMango