简体   繁体   English

为什么 Golang scheduler 使用两个 Queues(global run queue 和 local run queue)来管理 goroutine?

[英]Why Golang scheduler uses two Queues (global run queue and local run queue) to manage goroutine?

I was reading how Golang internally manages new created goroutine in the application.我正在阅读 Golang 如何在内部管理应用程序中新创建的 goroutine。 And I come to know runtime scheduler use to queue to manage the created goroutines.而且我开始知道运行时调度程序使用队列来管理创建的 goroutines。

  1. Global run queue: All newly created goroutine is placed to this queue.全局运行队列:所有新创建的goroutine都放在这个队列中。

  2. Local run queue: All go routine which is about to run is allocated to local run queue and from there scheduler will assign it to OS thread.本地运行队列:所有即将运行的 go 例程都分配到本地运行队列,调度程序将从那里将其分配给操作系统线程。

So, Here my question is why scheduler is using two queues to manage goroutine.所以,我的问题是为什么调度程序使用两个队列来管理 goroutine。 Why can't they just use global run queue and from there scheduler will map it to OS thread.为什么他们不能只使用全局运行队列,调度程序将从那里将 map 发送到操作系统线程。

First, please note that this blog is an unofficial and old source, so the information in it shouldn't be taken as totally accurate with respect to the current version of Go (or any version, for that matter).首先,请注意这个博客是一个非官方的旧来源,因此对于当前版本的 Go(或任何版本,就此而言),不应将其中的信息视为完全准确。 You can still learn from it, but the Go scheduler is improved over time, which can make information out of date.您仍然可以从中学习,但 Go 调度程序会随着时间的推移而改进,这可能会使信息过时。 For example, the blog says " Go scheduler is not a preemptive scheduler but a cooperating scheduler ".比如博客说“ Go scheduler is not a preemptive scheduler but a cooperating scheduler ”。 As of Go 1.14 , this is no longer true as preemption was added to the runtime.Go 1.14 开始,这不再是真的,因为抢占被添加到运行时。 As for the other information, I won't vouch for it's accuracy, but here's an explanation of what they say.至于其他信息,我不保证它的准确性,但这里是对他们所说内容的解释。


Reading the blog post:阅读博文:

There are two different run queues in the Go scheduler: the Global Run Queue (GRQ) and the Local Run Queue (LRQ). Go调度器中有两个不同的运行队列:全局运行队列(GRQ)和本地运行队列(LRQ)。 Each P is given a LRQ that manages the Goroutines assigned to be executed within the context of a P. These Goroutines take turns being context-switched on and off the M assigned to that P. The GRQ is for Goroutines that have not been assigned to a P yet.每个 P 都有一个 LRQ,它管理分配给在 P 的上下文中执行的 Goroutines。这些 Goroutines 轮流打开和关闭分配给该 P 的 M。GRQ 用于尚未分配给的 Goroutines一个P呢。 There is a process to move Goroutines from the GRQ to a LRQ that we will discuss later.有一个将 Goroutines 从 GRQ 移动到 LRQ 的过程,我们将在后面讨论。

This means the GRQ is for Goroutines that haven't been assigned to run yet, the LRQ is for Goroutines that have been assigned to a P to run or have already begun executing.这意味着 GRQ 用于尚未分配给运行的 Goroutines,LRQ 用于已分配给 P 运行或已经开始执行的 Goroutines。 Each Goroutine will start on the GRQ, and join a LRQ later to begin executing.每个 Goroutine 都会在 GRQ 上启动,稍后加入一个 LRQ 开始执行。

Here is the process that the previous quote was referencing, where Goroutines are moved from the GRQ to LRQ:这是之前引用的过程,其中 Goroutines 从 GRQ 移动到 LRQ:

In figure 10, P1 has no more Goroutines to execute.在图 10 中,P1 没有更多的 Goroutines 可以执行。 But there are Goroutines in a runnable state, both in the LRQ for P2 and in the GRQ.但是在可运行的 state 中有 Goroutines,在 P2 的 LRQ 和 GRQ 中。 This is a moment where P1 needs to steal work.这是 P1 需要窃取工作的时刻。 The rules for stealing work are as follows.窃取工作的规则如下。

 runtime.schedule() { // only 1/61 of the time, check the global runnable queue for a G. // if not found, check the local queue. // if not found, // try to steal from other Ps. // if not, check the global runnable queue. // if not found, poll.network. }

This means a P will prioritize running goroutines in their own LRQ, then from other P's LRQ, then from the GRQ, then from.network polling.这意味着一个 P 将优先在自己的 LRQ 中运行 goroutines,然后是其他 P 的 LRQ,然后是 GRQ,最后是来自网络轮询。 There is also a small chance to immediately run a Goroutine from the GRQ immediately.也有很小的机会立即从 GRQ 立即运行一个 Goroutine。 By having multiple queues, it allows this priority system to be constructed.通过拥有多个队列,它允许构建这个优先级系统。

Why do we want priority in which goroutines get run?为什么我们想要 goroutines 运行的优先级? It may have various performance benefits.它可能具有各种性能优势。 For example, it could make better use of the CPU cache.例如,它可以更好地利用 CPU 缓存。 If you run a Goroutine that was already running recently, it's more likely that the data it's working with is still in the CPU cache, making it fast to access.如果你运行一个最近已经在运行的 Goroutine,它正在处理的数据更有可能仍在 CPU 缓存中,从而可以快速访问。 When you start up a new Goroutine, it may use or create data that isn't in the cache yet.当您启动一个新的 Goroutine 时,它可能会使用或创建尚未在缓存中的数据。 That data will then enter the cache and could evict the data being used by another Goroutine, which in turn causes that Goroutine to be slower when it resumes again.然后,该数据将进入缓存,并可能驱逐另一个 Goroutine 使用的数据,这反过来会导致该 Goroutine 在再次恢复时变慢。 In the pathological case, this is called cache thrashing , and greatly reduces the effective speed of the processor.在病态情况下,这称为缓存抖动,会大大降低处理器的有效速度。

Allowing the CPU cache to work effectively can be one of the most important factors in achieving high performance on modern processors, but it's not the only reason to have such a queue system.让 CPU 缓存有效工作可能是现代处理器实现高性能的最重要因素之一,但这并不是拥有这样一个队列系统的唯一原因。 In general, the more logical processes that are running at the same time (such as Goroutines in a Go program), the more resource contention will occur.一般来说,同时运行的逻辑进程越多(比如Go程序中的Goroutines),资源竞争就越多。 This is because the resources used by a process tend to be fairly stable over the runtime of the process.这是因为进程使用的资源在进程运行期间往往相当稳定。 In other words, every time you start a new process tends to increase the overall resource load, while continuing an already started process tends to maintain the resource load, and finishing a process tends to reduce the resource load.换句话说,每次启动一个新进程往往会增加整体资源负载,而继续一个已经启动的进程往往会保持资源负载,而完成一个进程往往会减少资源负载。 Therefore, prioritizing already running processes over new processes would tend to help keep the resource load in a manageable range.因此,将已经运行的进程优先于新进程往往有助于将资源负载保持在可管理的范围内。

It's analogous to the practical advice of "finish what you started".这类似于“完成你开始的事情”的实用建议。 If you have a lot of tasks to accomplish, it's more effective to complete them one at a time, or multitask just a handful of things if you can.如果你有很多任务要完成,一次完成一个任务会更有效,或者如果可以的话,多任务处理一小部分事情。 If you just keep starting new tasks and never finished the previous ones, eventually you have so many things going on at the same time that you feel overwhelmed.如果你只是不断开始新的任务而从未完成之前的任务,最终你会同时发生太多事情,以至于你会感到不知所措。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM