QCon全球软件开发大会8折优惠倒计时,购票立减¥1760!了解详情 >>> 了解详情
写点什么

Go 调度程序:Ms,Ps&Gs

2019 年 11 月 25 日

Go调度程序:Ms,Ps&Gs

1 基础

Go 运行时管理调度、垃圾收集和 goroutines 的运行时环境。在这里,我将只关注调度程序。


运行时调度器通过将它们映射到操作系统线程来运行 goroutines。Goroutines 是线程的轻量级版本,启动成本非常低。每一个 goroutine 都是由一个名为 G 的结构体描述的,它包含了跟踪其堆栈和当前状态所必需的字段。所以,G = goroutine。


运行时跟踪每个 G,并将它们映射到逻辑处理器上,命名为 P。P 可以被看作是一个抽象的资源或上下文,需要被获取,因此 OS 线程(称为 M 或机器)可以执行 G。你可以通过调用 runtime.GOMAXPROCS(numLogicalProcessors) 来控制运行时的逻辑处理器,如果你打算调整这个参数(或许不应该),设置一次并忘记它,因为它需要“停止一切”GC 暂停。


从本质上讲,操作系统运行线程,执行你的代码。Go 的诀窍是,编译器在不同的地方插入调用到 Go 运行时,例如通过通道发送一个值,对运行时包进行调用),这样就可以通知调度程序并采取行动。



2 Ms,Ps&Gs 之间的舞蹈

Ms、Ps 和 Gs 之间的交互有点复杂。看一下这个工作流程图:



在这里我们可以看到,对于 G 来说有两种类型的队列:在“schedt”结构中有一个全局队列(很少使用),并且每个 P 维护一个可运行的 G 队列。


为了执行一个 goroutine,M 需要保存上下文 P.机器,然后弹出它的 goroutines,执行代码。


当你安排一个新的 goroutine(做一个 go func()调用)时,它被放置到 P 的队列中。这里有一个有趣的偷工调度算法,当 M 完成了某个 G 的执行,然后它试图从队列中取出另一个 G,它是空的,然后它随机地选择另一个 P 并试图从它中偷取一半的可运行的 G!


当你的 goroutine 做一个阻塞的系统调用时,会发生一些有趣的事情。阻塞系统调用将被拦截,如果要运行 Gs,运行时将从 P 中分离出线程并创建一个新的 OS 线程(如果空闲线程不存在的话)来服务该处理器。


当一个系统调用恢复时,goroutine 被放回一个本地运行队列,线程会自动放置(意味着线程不会运行),并将自己插入到空闲线程列表中。


如果 goroutine 进行网络调用,运行时也会执行类似的操作。这个调用将被拦截,但是因为 Go 有一个集成的网络轮询器,它有自己的线程,它将被分配给它。


如果当前的 goroutine 被阻塞,那么运行时将运行一个不同的 goroutine:


  • 阻塞系统调用(例如打开一个文件),

  • 网络输入,

  • 通道操作,

  • 同步包中的原语。


3 调度程序跟踪

Go 允许跟踪运行时调度程序。这是通过 GODEBUG 环境变量完成的:


$ GODEBUG=scheddetail=1,schedtrace=1000 ./program
复制代码


下面是它给出的输出示例:


SCHED 0ms: gomaxprocs=8 idleprocs=7 threads=2 spinningthreads=0 idlethreads=0 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0  P0: status=1 schedtick=0 syscalltick=0 m=0 runqsize=0 gfreecnt=0  P1: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0  P2: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0  P3: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0  P4: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0  P5: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0  P6: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0  P7: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0  M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 helpgc=0 spinning=false blocked=false lockedg=-1  M0: p=0 curg=1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 helpgc=0 spinning=false blocked=false lockedg=1  G1: status=8() m=0 lockedm=0
复制代码


注意,它使用了与 G、M 和 P 以及它们的状态相同的概念,比如 P 的队列大小。通常,你不需要那么多的细节,所以你可以使用:


$ GODEBUG=schedtrace=1000 ./program
复制代码


此外,还有一个名为 go tool trace 的高级工具,它有一个 UI,允许我们探索,程序运行时正在做什么。


总结

本次跟大家分享的知识点就是以上这些,不知道大家有没有理解呢,有任何问题都可以跟我们留言沟通。


本文转载自公众号 360 云计算(ID:hulktalk)。


原文链接:


https://mp.weixin.qq.com/s/wq-nirhGJ6Wm076gIN6FNw


2019 年 11 月 25 日 11:31359

评论

发布
暂无评论
发现更多内容

哈希,茫茫人海,我一眼看到了你

dongge

Java 类

michaelliu

Java

初文,大浪淘沙

傅丞 Tony

作为程序员,有哪些写作平台值得推荐 ?B站也算吧

邓瑞恒Ryan

学习 创业 写作 知识管理 自我提升

在InfoQ开启写作之旅

张先亮-Hank

人工智能 随笔

网络编程方法

Ya

方法论 网络编程 socket

夏天将来,愿我们有足够的知识继续前进

Amon Lee

转任管理岗位后,还要不要从事编码工作?

MavenTalker

团队管理 个人成长 程序员人生 职业规划

工厂模式

Wen Wei

设计模式

LeetCode 120. Triangle

隔壁小王

算法 LeetCode

稀缺:我们为什么会陷入贫穷与忙碌

insight

读书笔记

原创 | DDD与分层

编程道与术

vue项目中遇到的依赖及其他问题

靖仙

Vue 前端 Web 前端开发

学会打破确定性思维,才能做得更好

松花皮蛋me

高效工作 10X工作法 精益开发

Java 类构造函数的调用顺序

michaelliu

Java

你的文章中为什么会有加粗的文字

小天同学

思考 写作 感悟

最佳实践 | Flink Forward 全球会议抢先看!

Apache Flink

大数据 flink AI 流计算 实时计算

Java 代码的组织机制

michaelliu

Java

屏幕适配插件:ScreenMatch基本使用和注意事项

Arch

PyFlink 社区扶持计划正式上线!

Apache Flink

大数据 flink 流计算 实时计算 大数据处理

Iceberg 在基于 Flink 的流式数据入库场景中的应用

Apache Flink

大数据 flink 流计算 实时计算 大数据处理

技术人员能力养成手记

MavenTalker

个人成长 程序人生 职业规划

原创 | OOAD范例:配置类设计

编程道与术

深度工作

insight

读书笔记

docker搭建lamp

刘磐石(刘坤鹏)

关于PHP内存溢出的思考

L

php

祝贺!两位 Apache Flink PMC 喜提 Apache Member

Apache Flink

大数据 flink 流计算 实时计算 大数据处理

聊聊技术人如何与甲方客户打交道

MavenTalker

程序员 程序人生 职业规划

经验可能反而阻碍你的新认知

孙苏勇

思考 读书

什么是全光架构?光纤KVM和分布式IP KVM系统知多少?

DT极客

我入驻InfoQ平台啦

BlueblueWings

移动应用开发的下一站

移动应用开发的下一站

Go调度程序:Ms,Ps&Gs-InfoQ