(译)Go 语言的工作窃取调度器

原文链接:Go's work-stealing scheduler

Go 调度程序的任务是在多个运行在一个或多个处理器上的系统线程上分发出可运行的 goroutine。在多线程计算中,调度已经出现了两种模式:工作共享与工作窃取。

  • 工作共享:当一个处理器创建新的线程时,它试图将一部分线程迁移到其他的处理器上执行,期望更充分的利用那些 IDLE 状态的处理器。
  • 工作窃取:未被充分利用的处理器会主动寻找其他处理器上的线程,并“窃取”一些线程。

与工作共享模式相比,工作窃取模式线程的迁移发生的频率更低。当所有的处理器都有工作要运行时,没有任何线程被迁移。一旦有了空闲处理器,就会考虑迁移。

Go 从 1.1 版本开始就有了一个工作窃取模式的调度程序,它是由 Dmitry Vyukov 贡献的。这篇文章将深入地解释什么是工作窃取的调度程序,以及如何实现一个。

调度的基础

Go有一个 M:N 的调度程序,它可以使用多核处理器。在任何时候,M 个 goroutine 都需要被分发在最多 GOMAXPROCS 个处理器上运行的 N 个系统线程上。Go调度程序使用以下术语来描述 goroutines、线程和处理器:

  1. G:goroutine
  2. M:系统线程(Machine)
  3. P:处理器

有一个特定于处理器的本地 goroutine 队列和一个全局的 goroutine 队列。每个系统线程都应该被分配给一个处理器,如果处理器被阻塞或被系统调用,那么可能处理器上会没有线程。在任何时候,最多有
GOMAXPROCS 个处理器被用于分配。任何时候,一个线程都只能运行在一个处理器上。如果有需要调度器也可以创建更多的线程。

  • image.png

每一轮的调度都只是找到一个可运行的 goroutine 并执行它。在每一轮的调度中,搜索都按照以下顺序进行:

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.
}

一旦找到一个可运行的 goroutine ,它就会被执行,直到被阻塞。

注意:看起来全局队列比局部队列优先级更高,但是偶尔检查全局队列只是为了避免系统线程在局部队列中的 goroutine 用尽前只调用局部 goroutine 队列中的 goroutine。

窃取

当一个 goroutine 被创建或一个已经存在的 goroutine 变为可以运行状态,它被推送到当前处理器上的一个可运行的 goroutines 队列中,当处理器执行完一个 goroutine,它将试图从自己的局部可运行 goroutine 队列中弹出这个 goroutine。如果队列为空,处理器会随机选择一个其他处理器,并试图从这个处理器的局部可运行 goroutine 队列中偷取一半数量的可运行 goroutine。

  • image.png

在上面的例子中,P2 这个处理器无法找到任何可执行的 goroutines。因此,它随机选择另一个处理器 P1,并将 3 个 goroutines 偷到自己的局部队列中。P2 将执行这些 goroutines,而调度器也将在多个处理器之间更均衡的调度。

旋转线程

调度程序总是希望将可运行的 goroutines 分发到线程中,以便充分利用处理器,但同时我们又需要限制过多的任务来节省 CPU 资源。与此相矛盾的是,调度器还需要能够扩展到高吞吐量和CPU密集的程序中。

如果性能很关键,那么经常性的抢占将显得十分的昂贵,并且对高吞吐量的程序来说这也是个严重的问题。操作系统线程之间不应该频繁的传递可运行的 goroutine,因为这将导致延迟的增加。另外,在有系统调用的情况下,系统线程需要不断的 block 与 unblock,这也是非常昂贵的同时也增加了很多额外的开销。

为了减少线程间传递,调度器实现了“旋转线程”。旋转线程虽然消耗了额外的 CPU 资源,但是它们最小化了操作系统线程的抢占。一个线程正在旋转,如果:

  1. 一个拥有线程的处理器正在寻找一个可执行的 goroutine。
  2. 一个没有所属处理器的线程正在寻找一个可以依附的处理器
  3. 当它准备一个 goroutine 时,如果有一个空闲的处理器并且没有其他的旋转线程,调度程序会取消一个额外的线程,然后旋转这个线程。

在任何时候,都有最多 GOMAXPROCS 个线程在旋转。当一个旋转的线程找到工作后,它就会脱离自旋状态。

如果空闲的线程没有处理器可分配,则已分配到处理器上的空闲线程不会阻塞。当新的 goroutine 被创建或者一个线程被阻塞时,调度程序将确保至少有一个旋转的线程,这确保了当没有可运行的 goroutine 时程序仍可以运行,也避免过多的线程 block/unblock。

Go调度程序做了很多工作,以避免对操作系统线程的过度抢占,通过将它们调度到正确的和未充分利用的处理器上,并实现了“旋转”线程,以避免出现阻塞/未阻塞的转换。
调度事件可以通过执行跟踪程序跟踪。如果你认为你的处理器利用率很低,那么你可以排查一下正在发生什么。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • Goroutine是Go里的一种轻量级线程——协程。相对线程,协程的优势就在于它非常轻量级,进行上下文切换的代价非...
    witchiman阅读 4,737评论 0 9
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • http://skoo.me/go/2013/11/29/golang-schedule?hmsr=studygo...
    baboon阅读 2,180评论 0 3
  • GCD调度队列是执行任务的强大工具。调度队列允许您相对于调度者异步或者同步的执行任意代码块。您能够使用调度队列来执...
    坤坤同学阅读 6,616评论 1 3
  • 我的PAT系列文章更新重心已移至Github,欢迎来看PAT题解的小伙伴请到Github Pages浏览最新内容。...
    OliverLew阅读 388评论 0 0