为什么 Go 被认为是部分抢占式的?

我试图在 Go 的上下文中更好地理解先发制人合作的定义。抢占式多任务处理的 Wiki 状态

在计算中,抢占是暂时中断正在执行的任务的行为,目的是稍后恢复它。此中断由外部调度程序完成,无需任务的协助或合作。

Wiki 指出“外部调度程序”。我想这意味着调度程序更具体,因为 afaik 调度程序只负责选择下一个进程。

Go 通常被称为部分抢占,因为同步点/抢占点仅在函数调用处而不是在任何给定指令处。这就说得通了。但正如 wiki 定义所述,抢占式是由外部调度程序完成的。

但是,难道不是每个进程或任务都是抢占式的吗,因为 CPU 可以在执行过程中停止执行任何进程以切换到另一个进程?任何澄清表示赞赏!

附录

我能想到的唯一解释是我们讨论了不同的抢占级别。一种用于进程,一种用于内核/用户线程。在这种情况下,CPU 调度程序选择下一个进程,但 Go 调度程序负责 goroutines/线程。


汪汪一只猫
浏览 102回答 3
3回答

眼眸繁星

你的附录是正确的。这里有不同的调度程序。一种是操作系统调度程序,另一种是应用程序级别。查看这篇文章。这正是您要寻找的:正如我们在第一篇文章中讨论的那样,操作系统调度程序是一种抢占式调度程序。[...] 内核正在做决定,一切都是不确定的。和这个:Go 调度程序是 Go 运行时的一部分,并且 Go 运行时内置于您的应用程序中。这意味着 Go 调度程序在内核之上的用户空间中运行。Go 调度器的当前实现不是抢占式调度器,而是协作式调度器。作为协作调度程序意味着调度程序需要在代码中的安全点发生的定义明确的用户空间事件来做出调度决策。结论是,有两个不同的调度器。一个用于进程,一个用于 Goroutines。

呼如林

这里有几件事。首先,维基百科文章谈论的是操作系统级别的抢占,这可能并不完全适用。Go 例程调度不是由操作系统处理,而是由 Go 运行时处理。本质上,进程/线程由操作系统/硬件处理并且是抢占式的。Go 运行时可以在同一个线程上运行不同的“go routines”。(事实上 ,这是使 Go 独一无二的一件事 - 轻松允许您创建具有数百万个并发 go-routines 的应用程序。)最初 go-routines 不是抢占式的,但令人惊讶的是这导致了一些问题,但是没有遇到抢占点的长时间运行的循环可能会占用线程。但多亏了 Austin Clements 的一些出色工作,这个问题在几年前就得到了解决(Go 1.15?)。所以我相信 go-routines 至少在更流行的架构上是完全可抢占的,但你可能会遇到旧的评论,否则会这样说。我希望这有助于回答您的具体问题:但不是每个进程或任务都是抢占式的……吗?是的,但它们不是例程。CPU 调度程序选择下一个进程,但 Go 调度程序负责 goroutines/线程。不,操作系统(使用 CPU/MMU 硬件设施)调度进程和线程。Go 调度程序只决定在它控制的特定线程上接下来运行哪个 go-routine,并且它永远不会控制超过 GOMAXPROCS(非阻塞)线程。

慕哥6287543

我的回答建立在 Andrew 和 Daniel 的回答之上,并且完全基于这次谈话。从 go 1.14 开始,go 调度器是非合作抢占的。每个 go routine 在一定的时间片后被抢占。在 go 1.19.1 中是10ms 。在我之前提到的演讲中,从 20:35 开始,您可以找到关于调度程序如何在 1.0 中使用纯协作抢占的历史,然后是编译器在 go 1.2 中烘焙抢占,最后是当前的性质 - 非-合作先发制人。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go