Golang Go语言是协作式调度,还是抢占式调度?

发布于 1周前 作者 zlyuanteng 来自 Go语言

首先,介绍我对 GoLang 调度过程的认识,如果有误,请指出。

GoLang 的调度模型是 GMP 调度模型,调度的计算实体是 Goroutine ,调度的资源载体是 Machine (线程)。用户创建的 Goroutine 会被提交至 Processor ,Processor 会根据当前空闲与否决定将 Goroutine 是否调度到一个线程执行,当 Processor 上当前线程执行的 Goroutine 结束或挂起时,则会调度新的 Goroutine 至某线程。

接下来,介绍我的疑惑:

  • 一方面,当协程退出或 IO 阻塞时主动让出占用的计算资源,供其他 Goroutine 使用,这是典型的协作式调度。
  • 另一方面,当一个协程长时间占用计算资源,runtime 会将其强制中断,进而让其他 Goroutine 都能有机会得到执行,这应该是抢占式调度。

从以上两方面,感觉 Go 既有协作式?又有抢占式?但是为什么大家都说 Go 是协作式呢?

站内大佬颇多,还请解惑~


Golang Go语言是协作式调度,还是抢占式调度?

更多关于Golang Go语言是协作式调度,还是抢占式调度?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

22 回复

印象里 go 1.14 有一个 update 。转向 preemptive

更多关于Golang Go语言是协作式调度,还是抢占式调度?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你感觉是对的呀,相对传统线程,goroutine 有协作式调度;相对传统 coroutine ,goroutine 也可以抢占式调度。

这不就跟某知名运动员一样,在中国是中国人,在美国是美国人。

大家都说他是协作式,那是因为传统的并发方式是多进程和多线程,相对他们而言的。

协作式调度是在后面的版本才加进来的吧。(加入了协作调度功能,被营销号吹成 转成协作调度)
之前的版本都是抢占,所以两种方式现在都有。

对 goroutine 来说应该是抢占式,IO 调用并没有主动让出的意思,而是被 runtime 插入的调度点。Gosched()才算协作。

因为 go 会不断更新啊,你参考的信息又不一定是最新的

你是说反了吗,应该是老版本是协作式调度,后面才加入的抢占式调度

> Goroutines are now asynchronously preemptible. As a result, loops without function calls no longer potentially deadlock the scheduler or significantly delay garbage collection. ( https://go.dev/doc/go1.14)


Goroutine 现在会在编译的时候被编译器插入类似 Gosched() (实际是用 semaphore )

大家都说 go 的是抢占式吧 相对于别的协程

你对于 GoLang 的调度过程和 GMP 调度模型的理解基本上是正确的。

关于你的疑惑,GoLang 的调度模型的确是协作式调度模型。虽然在某些情况下,GoLang 会使用抢占式调度来强制中断长时间占用计算资源的 Goroutine ,但这并不代表整个调度模型是抢占式的。

在 GoLang 的调度模型中,每个 Goroutine 都有一个 Goroutine 栈和调度器关联。当 Goroutine 遇到阻塞或等待 IO 时,调度器会将该 Goroutine 标记为不可运行,让出 CPU 资源给其他可运行的 Goroutine 。当 IO 操作完成或其他等待条件满足时,该 Goroutine 再次标记为可运行,调度器会在适当的时候重新调度该 Goroutine 。

同时,GoLang 的调度器会根据一定的规则,例如时间片轮转算法和休眠唤醒机制等,在运行中的 Goroutine 之间进行切换,保证每个 Goroutine 都能得到执行机会。这种协作式调度模型能够更好地利用 CPU 资源,避免了不必要的上下文切换开销,也避免了抢占式调度可能带来的问题,例如死锁和资源争用等。

综上所述,尽管 GoLang 的调度模型在某些情况下会使用抢占式调度,但整个调度模型仍然是协作式调度模型。因此,大家通常会将 GoLang 的调度模型称为协作式调度模型。

我帮你问了 chatgpt4

你对 GoLang 调度过程的认识基本正确。的确,Go 语言的调度模型是 GMP 模型,涉及到 Goroutine ( G )、Processor ( P )和 Machine ( M )三个主要概念。

关于你的疑惑,Go 语言的调度机制主要是协作式调度。然而,从 Go 1.14 版本开始,Go 在协作式调度的基础上加入了一些抢占式调度的特性。这主要体现在 goroutine 抢占方面,通过在执行过程中插入抢占点(例如函数调用)来实现。这样,当一个 Goroutine 长时间占用计算资源时,runtime 可以强制中断它,让其他 Goroutine 有机会得到执行。

尽管 Go 语言引入了这些抢占式调度特性,但它仍然主要是协作式调度。这是因为 Goroutine 在许多情况下主动让出计算资源(例如,当遇到 IO 阻塞时),这是协作式调度的核心特点。而抢占式调度特性只是在特定情况下作为补充,以确保在某些场景下调度的公平性和响应性。

因此,大家通常认为 Go 语言主要采用协作式调度,而抢占式调度仅在特定情况下作为辅助。这也是为什么大家说 Go 是协作式调度的原因。

#10 这个应该也是 gpt 的回答 哈哈

笑死,因为我也想知道答案,但是网上根本没有答案

协作式,但是为了一些场景一直不让出控制权,后续加入了一定的抢占的场景

你俩是真不怕封号……

这里我比较有兴趣,后面也查了下资料

从 go 1.14 开始,go 调度器是非合作抢占的。每个 goroutine 在一定的时间片后被抢占。在 go 1.19.1 中是 10ms 。
代码在这里: https://github.com/golang/go/blob/go1.19.1/src/runtime/proc.go#L5279-L5281

混合式。

但 golang 是以能方便的写协作调度出名的。

#15 帮 op 问一问只回一个 不是机器人自动回啊。 😭

“协作式”调度时,指的是谁在协作?应该是指用户协程,而不是 runtime. 当一个 goroutine 陷入 IO 调用或者 prologue ,它本身是没有让出计算资源的主观意愿的,这一点可以对比显式的 Gosched() 调用和其它语言的 await. 同样也可以类比系统线程,在系统调用返回前,计算资源可能被释放并切换到其它线程,这应该也不算协作,因为线程并没有想要释放资源,是操作系统的决策。

协作式效率高,协程抢占式没意义

我记得抢占式是有意义的, 协作式是在函数调用时,检查是否让出执行, 就会出现一种情况

<br>func main() {<br> runtime.GOMAXPROCS(1)<br> go func() {<br> for {<br> }<br> }()<br> time.Sleep(time.Millisecond)<br> println("OK")<br>}<br>
这段代码在 go1.14 之前不会打印 OK 的 就是因为协程中没有函数调用,也就没有协作式的检查点, 协程就不会退出

针对“Golang Go语言是协作式调度,还是抢占式调度?”这一问题,作为IT营GO语言方面的专家,我给出以下回答:

Golang的调度策略实际上结合了协作式调度和抢占式调度。

在协作式调度方面,Goroutine(Go语言的轻量级线程)会在适当的时机主动让出CPU,以便其他Goroutine可以运行。这种调度方式提高了系统的并发性能。

然而,Golang的调度器也采用了抢占式调度策略。这意味着任何一个Goroutine的执行都可能被其他Goroutine随时中断。抢占式调度策略能够合理分配CPU资源,防止某个Goroutine长时间独占CPU而导致其他Goroutine无法执行。当一个Goroutine被抢占时,调度器会将其状态保存,并切换到其他可执行的Goroutine。

因此,Golang的调度机制是灵活且高效的,它结合了协作式调度和抢占式调度的优点,以最大程度地提高系统的并发性能和资源利用率。

以上内容仅供参考,如需更多关于Golang调度机制的信息,建议查阅官方文档或咨询专业开发人员。

回到顶部