golang 重要知识:golang 调度

简介: Go 的调度机制相当于我们微服务里的基础组件。很多运行时操作都涉及到了调度的关联。本文会细聊调度概念,策略,以及它的机制。当然,也少不了最常提及的 GMP 模型。

摘要

Go 的调度机制相当于我们微服务里的基础组件。很多运行时操作都涉及到了调度的关联。本文会细聊调度概念,策略,以及它的机制。当然,也少不了最常提及的 GMP 模型。

一、调度是什么?

计算机的资源有限的,像 CPU,内存都是固定的。但是同一时间可能会有多个任务要去完成,比如操作系统的定时监控,用户程序的运行等。

怎么让资源最大化的完成任务,这是调度需要考虑的关键点。

调度可以理解为一个指挥员,指导我们的程序按照一定的规则去获取资源,然后去执行里面的指令。

调度分配

那么,一般的规则有哪些呢?

常见的调度策略有 2 种,一种是协作式调度,会让程序顺利的完成自己的任务,再把资源腾出来给其他程序使用。

另一种是抢占式调度,也就是让程序按一定的时间去占有这些资源,时间到了就被迫让出现有资源,给其他的程序轮流使用。

协作式调度有利于程序专注的完成自己的任务,但也可能会造成其他程序一直饿死,得不到执行。
协作式调度

抢占式调度有利于程序在资源的利用上雨露均沾,但是在不断的切换过程中,将会使得程序原本 10 ms 能完成的事,不得不延迟多几 ms。

抢占式调度

注:Linux 操作系统也是采用了抢占式调度,并且使用了 CFS:完全公平调度算法。通过对程序大致的运行时间来 平衡调度,让越没有执行过的程序,越快被调度到。

当前大多数操作系统都是采用抢占式调度来执行程序的,毕竟很多操作系统都是面向用户,需要很高的响应速度,而且只要切换程序的周期够短,例如 50ms,那对于用户来讲,就像没切换一样。

二、golang 的调度

上面提及到抢占式调度会有个频繁切换的过程,在切换时,需要不断的保存或恢复上下文信息。

而这会涉及到操作系统内核态和用户态的切换,性能损耗会很大。

对此,golang 实现了属于自己的调度模型,采用了基于协作的抢占式调度。之所以是"协作"的,是因为 Go 的调度时机是由用户自己设置的,而这里的用户指的是 golang 的运行时 runtime

它会在下面的事件发生时进行调度触发:

  • 使用关键字 go
  • 垃圾回收
  • 系统调用,如访问硬盘
  • 同步阻塞调用,如 使用 mutex、channel

如果上面什么事件都没发生,则会有 sysmon 来监控 goroutine 的运行情况,对长时间运行的 goroutine 进行标记。一旦 goroutine 被标记了,那么它就会下次发生函数调用时,将自己挂起,再触发调度。

这里需要说明下的是,runtime 它相当于 Java 的虚拟机,负责了 Go 的很多东西,例如调度垃圾回收、内存管理等,可以说是涵盖了 Go 的基础引擎了。

更重要的是 runtime 是运行在用户态上的,相当于 Go 的调度是在用户态这一层进行的。

这样,每当 Go 有调度产生时,就不会伴随着用户态和内核态的切换,而是像前面提到过的策略那样去触发调度,这就降低了并发时的内核态与用户态的切换成本了。

三、golang 的 GPM 模型

为了实现 golang 的调度,golang 抽象出了三个结构,也就是我们常见的 G、P、M

G:也就是协程 goroutine,由 Go runtime 管理。我们可以认为它是用户级别的线程。

goroutine 非常的轻量,初始分配只有 2KB,当栈空间不够用时,会自动扩容。同时,自身存储了执行 stack 信息、goroutine 状态以及 goroutine 的任务函数等。

P:processor 处理器。P 的数量默认跟 CPU 的核心数一样,如果是多核的 CPU,则会有多个 P 会被创建。

每当有 goroutine 要创建时,会被添加到 P 上的 goroutine 本地队列上,如果 P 的本地队列已满,则会维护到全局队列里。

在进行调度时,会优先从本地队列获取 goroutine 来执行。

如果本地队列没有,会从其他的 P 上偷取 goroutine。

如果其他 P 上也没有,则会从全局队列上获取 goroutine。

这样通过上面的策略,就能尽最大努力保证有 goroutine 可运行

M:系统线程。在 M 上有调度函数,它是真正的调度执行者,M 需要跟 P 绑定,并且会让 P 按上面的原则挑出个 goroutine 来执行。

M 虽然从 P 上挑选了 G 执行,但 M 并不保存 G 的上下文信息,而是 G 自己保存了相关信息,这样有利于转移到其他 M 上,在不同的 M 上运行。

GPM模型

GPM 模型的优势点在于 G 包含了执行任务相关信息,M 提供了执行环境,并且有调度机制。而 P 则是他们两者的粘合剂。

假如没有 P 。那么 M 就会有争夺 G 的竞争问题,并且 M 的数量会不可控,会出现过多的 M 去处理 G。

一旦超过了 CPU 的核心数,那么就会将性能耗费在上下文切换过程中。

有了 P 这一层后,M 优先从 P 的本地队列获取 goroutine,减少并发竞争。并且保证了最多跟 CPU 核心数一样的 goroutine 数量在并行运行,充分利用了多核优势,又不被滥用。

总结

相信看过本文后,各位对 Golang 的调度有了一定的了解。正是因为基于协作的抢占式调度和 GMP 模型,Golang 的高并发高性能才有了底层保障。当然,大伙也可以深入到源码去分析这些调度机制,这样离大神就更近一步了 ㋡...

相关文章
|
3月前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
106 1
|
6月前
|
NoSQL Unix 编译器
Golang协程goroutine的调度与状态变迁分析
文章深入分析了Golang中goroutine的调度和状态变迁,包括Grunnable、Gwaiting、Grunning和Gsyscall等状态,以及它们之间的转换条件和原理,帮助理解Go调度器的内部机制。
80 0
|
9月前
|
监控 编译器 Linux
golang面试:golang的GPM调度模型(七)
golang面试:golang的GPM调度模型(七)
77 1
|
存储 负载均衡 数据可视化
[典藏版]深入理解Golang协程调度GPM模型
<深入理解Golang协程调度器GPM模型>介绍了Golang中调度器的由来,以及如何演进到GPM模型的设计,其中包含一个Go协程在启动过程中如何运行和加载GPM模型的细节动作,也包括GPM模型的可视化编程和调试分析。最后形象介绍GPM模型的各个触发条件及运作的场景。
361 1
[典藏版]深入理解Golang协程调度GPM模型
|
存储 负载均衡 数据可视化
[典藏版]深入理解Golang协程调度GPM模型
《深入理解Golang协程调度器GPM模型》介绍了Golang中调度器的由来,以及如何演进到GPM模型的设计,其中包含一个Go协程在启动过程中如何运行和加载GPM模型的细节动作,也包括GPM模型的可视化编程和调试分析。最后形象介绍GPM模型的各个触发条件及运作的场景。
526 1
[典藏版]深入理解Golang协程调度GPM模型
|
4天前
|
存储 监控 算法
探秘员工泄密行为防线:基于Go语言的布隆过滤器算法解析
在信息爆炸时代,员工泄密行为对企业构成重大威胁。本文聚焦布隆过滤器(Bloom Filter)这一高效数据结构,结合Go语言实现算法,帮助企业识别和预防泄密风险。通过构建正常操作“指纹库”,实时监测员工操作,快速筛查可疑行为。示例代码展示了如何利用布隆过滤器检测异常操作,并提出优化建议,如调整参数、结合日志分析系统等,全方位筑牢企业信息安全防线,守护核心竞争力。
|
5天前
|
监控 Linux PHP
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
54 20
|
11天前
|
监控 关系型数据库 MySQL
【01】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-硬件设备实时监控系统运营版发布-本产品基于企业级开源项目Zabbix深度二开-分步骤实现预计10篇合集-自营版
【01】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-硬件设备实时监控系统运营版发布-本产品基于企业级开源项目Zabbix深度二开-分步骤实现预计10篇合集-自营版
20 0
|
12天前
|
Go C语言
Go语言入门:分支结构
本文介绍了Go语言中的条件语句,包括`if...else`、`if...else if`和`switch`结构,并通过多个练习详细解释了它们的用法。`if...else`用于简单的条件判断;`if...else if`处理多条件分支;`switch`则适用于基于不同值的选择逻辑。特别地,文章还介绍了`fallthrough`关键字,用于优化重复代码。通过实例如判断年龄、奇偶数、公交乘车及成绩等级等,帮助读者更好地理解和应用这些结构。
34 14
|
26天前
|
存储 监控 算法
内网监控系统之 Go 语言布隆过滤器算法深度剖析
在数字化时代,内网监控系统对企业和组织的信息安全至关重要。布隆过滤器(Bloom Filter)作为一种高效的数据结构,能够快速判断元素是否存在于集合中,适用于内网监控中的恶意IP和违规域名筛选。本文介绍其原理、优势及Go语言实现,提升系统性能与响应速度,保障信息安全。
28 5

热门文章

最新文章