揭秘系列: Goroutine调度器

现在不要担心理解上面的图片,因为我们将从非常基础的知识开始。

Goroutines分布在线程中,由Goroutine调度器在幕后处理。根据我们之前的讨论,我们知道一些关于Goroutines的事情:

?从原始执行速度来看,Goroutines不一定比线程更快,因为它们需要一个实际的线程来运行。?Goroutines的真正优势在于上下文切换、内存占用、创建和拆除的成本等方面。

你可能之前听说过Goroutine调度器,但我们真正了解它是如何工作的吗?它是如何将Goroutines与线程配对的?

现在让我们一步一步地分解调度器的操作。

1. Goroutine的M:N调度器

Go团队为我们真正简化了并发处理,想想看:创建一个Goroutine就像在函数前面加上 go 关键字一样容易。

go doWork()

但在这个简单的步骤背后,有一个更深层的系统在运作。

从一开始,Go并不是简单地提供了线程。相反,在中间有一个辅助工具,Goroutine调度器,它是Go运行时的关键部分。

揭秘系列: Goroutine调度器

1*6UyqGhkbOV7kSlRe1CD-gA.png

那么什么是M:N标签?

它表示Go调度器在将M个Goroutines映射到N个内核线程时所起的作用,形成了M:N模型。你可以拥有更多的操作系统线程,就像可以拥有更多的Goroutines一样。

在我们深入研究调度器之前,让我们澄清一下经常混淆的两个术语:并发和并行。

?并发:这是关于同时处理多个任务,它们都在运动,但不总是在同一时刻。?并行:这意味着许多任务在完全相同的时间运行,通常使用多个CPU核心。

揭秘系列: Goroutine调度器

1*30ViMAPkVySvdSDc-hI3sA.png

让我们看看Go调度器是如何与线程配合运作的。

2. PMG 模型

在我们深入研究内部工作原理之前,让我们分解一下P、M和G代表什么。

G(Goroutine)

Goroutine充当Go的最小执行单元,类似于轻量级线程。

在Go的运行时中,它由一个名为gstruct{}表示。一旦创建,它会找到一个逻辑处理器P的本地可运行队列(或G队列)中的位置,然后P将其交给一个实际的内核线程M。

Goroutines通常存在三种主要状态:

?等待:在这个阶段,Goroutine停滞不前,可能因为通道或锁之类的操作而暂停,或者可能被系统调用暂停。?可运行:Goroutine已经准备好运行,但尚未开始运行,它正在等待轮到它在一个线程(M)上运行。?运行:现在,Goroutine正在一个线程(M)上积极执行。它会一直执行,直到任务完成,除非调度器中断它或其他原因阻止了它的执行。

揭秘系列: Goroutine调度器

1*U62eyES6_koQtsv_9jWKHw.png

Goroutines并不仅仅被使用一次然后被丢弃。

相反,当启动新的Goroutine时,Go的运行时会从Goroutine池

中选择一个,但如果找不到任何可用的Goroutine,它会创建一个新的。然后,这个新的Goroutine加入到一个P的可运行队列中。

P(逻辑处理器)

在Go调度器中,当我们提到“处理器”时,我们指的是逻辑实体,而不是物理实体。

默认情况下,P的数量设置为可用的核心数,你可以使用runtime.GOMAXPROCS(int)函数来检查或更改这些处理器的数量。

runtime.GOMAXPROCS(0) // 获取当前允许的逻辑处理器数量

如果你想更改这个数量,最好是在应用程序启动时更改它,如果在运行时更改,它会导致STW(停止一切),直到重新调整处理器。

每个P都拥有自己的可运行Goroutines列表,称为本地运行队列,最多可以容纳256个Goroutines。

揭秘系列: Goroutine调度器

1*0EneA397HA1uYYeg0_HspQ.png

调度器 — P(逻辑处理器)

如果P的队列达到了最大Coroutines数(256),那么就有一个共享队列,称为全局运行队列,但我们将稍后讨论这个。

\\”那么 \\’P\\’ 的这个数量到底代表什么?\\” 它表示可以同时运行的Goroutines数量 — 想象它们并行运行。

M(机器线程 — 操作系统线程)

一个典型的Go程序最多可以使用1万个线程。

是的,我说的是线程,而不是Goroutines。如果超出这个限制,你可能会使你的Go应用程序崩溃。

\\”什么情况下会创建一个线程?\\” 想象一种情况:一个Goroutine处于可运行状态并需要一个线程。如果所有线程已经被阻塞,可能是因为系统调用或非抢占操作,会怎么样?在这种情况下,调度器会介入并为该Goroutine创建一个新线程。一个需要注意的事情是,如果一个线程只是忙于昂贵的计算或长时间运行的任务,它不被视为被卡住或被阻塞。如果你想更改默认线程限制,你可以使用 runtime/debug.SetMaxThreads() 函数,它允许你设置你的Go程序可以使用的操作系统线程的最大数量。此外,值得知道的是,线程是可以重复使用的,因为创建或销毁线程是消耗资源的。

3. MPG 工作原理

让我们通过项目符号逐步了解 M、P 和 G 如何一起运作。

我不会在这里深入讨论每个细节,但我将在即将发布的故事中深入探讨。如果你感兴趣,请订阅。

揭秘系列: Goroutine调度器

1*d4hu416FJtHHaJaKJFYkGg.png

Go Scheduler 的工作原理

1.初始化 goroutine:通过使用 go func() 命令,Go Runtime 要么创建一个新的 goroutine,要么从池中选择一个现有的。2.排队位置:goroutine 寻找其在队列中的位置,如果所有逻辑处理器(P)的本地队列都满了,那么这个 goroutine 就被放入全局队列。3.线程配对:这是 M 开始发挥作用的地方。它获取一个 P 并开始处理来自 P 本地队列的 goroutine,当 M 与这个 goroutine 交互时,与之关联的 P 就变得占用,不再可用于其他 M。4.窃取行为:如果某个 P 的队列被耗尽,M 会尝试“借用”另一个 P 队列中一半可运行的 goroutine。如果不成功,它然后检查全局队列,然后再检查网络轮询器(请查看下面的“窃取过程”图表部分)。5.资源分配:M 选择了一个 goroutine(G)之后,它会获取运行 G 所需的所有资源。

“那么被阻塞的线程呢?” 如果一个 goroutine 启动了需要时间的系统调用(比如读取文件),那么 M 会等待。但调度程序不喜欢某个只是坐在那里等待的线程,它会将被暂停的 M 与其 P 解除连接,并将来自队列的另一个可运行的 goroutine 与新的或现有的 M 连接起来,然后与 P 协作。

被阻塞的线程

窃取过程

当一个线程(M)完成了它的任务并没有其他事情可做时,它不会坐在那里。

相反,它积极地寻找更多工作,观察其他处理器并获取它们一半的任务,让我们来详细了解一下:

ewA.png

1.每 61 个时钟滴答,M 检查全局可运行队列,以确保执行的公平性。如果在全局队列中找到一个可运行的 goroutine,就停止。2.然后,线程 M 检查其本地运行队列,与其处理器 P 相关联,以查看是否有可运行的 goroutine 可以处理。3.如果线程发现它的队列是空的,那么它会查看全局队列,看看那里是否有等待处理的任务。4.然后,线程会检查网络轮询器,以查看是否有与网络相关的任务。5.如果线程在检查了网络轮询器后仍然没有找到任务,它将进入主动搜索模式,我们可以将其视为旋转状态。6.在这种状态下,线程试图从其他处理器的队列中“借用”任务。7.经过所有这些步骤后,如果线程仍然找不到工作,它将停止主动搜索。8.现在,如果有新的任务进来,而且有一个没有在搜索状态的空闲处理器,那么可以提示另一个线程开始工作。

需要注意的细节是全局队列实际上被检查了两次:每 61 个时钟滴答一次以确保公平性,如果本地队列为空,就再次检查。

“如果 M 与其 P 相关联,它怎么能从其他处理器那里获取任务呢?M 会更改其 P 吗?” 答案是不会。即使 M 从另一个 P 的队列中获取任务,它仍然使用其原始处理器来运行该任务。因此,在 M 承担新任务的同时,它仍然忠实于其处理器。

“为什么是 61?” 在设计算法时,特别是哈希算法,通常会选择质数,因为它们除了 1 和它们自己之外没有除数。这可以降低出现模式或规律的机会,从而防止“碰撞”或其他不希望出现的行为。如果太短,系统可能会浪费资源频繁检查全局运行队列。如果太长,goroutine 可能会在执行之前等待过长的时间。

网络轮询器

我们还没有详细讨论网络轮询器,但它在窃取过程图表中提到了。

与 Go Scheduler 一样,网络轮询器是 Go Runtime 的组成部分,负责处理与网络相关的调用(例如,网络 I/O)。

让我们比较两种系统调用类型:

?与网络相关的系统调用:当一个 goroutine 执行网络 I/O 操作时,它不会阻塞线程,而是会在网络轮询器中注册。轮询器会异步等待操作完成,一旦完成,goroutine 就会再次可运行,可以在一个线程上继续执行。?其他系统调用:如果它们可能会阻塞并且不由网络轮询器处理,它们可能会导致 goroutine 将其执行卸载到操作系统线程上。只有特定的操作系统线程会被阻塞,Go 运行时调度程序可以在不同线程上执行其他 goroutine。

原创文章,作者:小技术君,如若转载,请注明出处:https://www.sudun.com/ask/33866.html

(0)
小技术君's avatar小技术君
上一篇 2024年4月16日 下午3:55
下一篇 2024年4月16日 下午3:57

相关推荐

  • 什么是KMP?

    部分匹配表 当然,KMP 的关键是部分匹配表。我和理解 KMP 之间的主要障碍是我没有完全理解部分匹配表中的值的真正含义。我现在将尝试用最简单的词来解释它们。 这是模式“ababa…

    CDN资讯 2024年4月15日
    0
  • 解密gRPC: Protocol Buffer

    在之前的文章中,我们全面介绍了gRPC,在这一部分中,我们将涵盖Protocol Buffer,也称为Protobuf。 0*A9XtTt1Mo_a7CCy8.pngProtobu…

    2024年4月3日
    0
  • 云CDN,阿里云cdn

    在当今数字时代,云CDN已然成为网络优化的焦点话题。在这篇文章中,我们将深入探讨云CDN的重要性以及它在解决网站速度和性能问题方面的作用。随着互联网用户数量的不断增长,网站的速度和…

    2024年5月11日
    0
  • cdn资质申请条件,宽带维修上门附近电话

    标题:CDN资质申请条件 首段:在网站建设中,CDN(内容分发网络)的应用越来越重要,但要想正式使用CDN服务,必须了解并满足一定的资质申请条件。本文将详细介绍CDN资质申请的相关…

    2024年5月11日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注