Golang的GMP调度模型与源码解析

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。

一、GMP 调度模型概述


  1. 背景介绍
  • 在 Go 语言中,为了高效地利用多核处理器并管理大量的并发任务,引入了 GMP 调度模型。它是 Go 语言运行时(runtime)系统的核心部分,用于管理和调度 Go 协程(goroutine)。
  • 与传统的操作系统线程调度相比,Go 的 GMP 调度模型能够在少量操作系统线程(OS Thread)的基础上,高效地调度大量的轻量级协程,从而实现高性能的并发处理。
  1. 基本概念
  • G(Goroutine):Go 语言中的协程,是一种轻量级的用户态线程。它由 Go 运行时进行调度,相比操作系统线程,其创建和销毁的成本非常低。例如,在一个网络服务器应用中,可以轻松创建数千个协程来处理并发的客户端连接,而不会像创建数千个操作系统线程那样带来巨大的资源开销。
  • M(Machine):代表操作系统线程。Go 运行时会将协程调度到操作系统线程上执行。M 与操作系统的线程一一对应,它是真正执行计算的实体。
  • P(Processor):可以看作是逻辑处理器。它是连接 G 和 M 的桥梁,每个 P 都有一个本地队列,用于存放等待执行的 G。P 的数量通常与系统的 CPU 核心数相关,通过这种方式可以充分利用多核处理器的性能。

二、GMP 调度模型的工作流程


  1. G 的创建与初始化
  • 当使用go关键字创建一个协程时,Go 运行时会分配一个 G 结构体来表示这个协程。这个 G 结构体包含了协程的栈空间、程序计数器、状态等信息。
  • 例如,go func() { println("Hello, world") }()这个语句会创建一个新的协程,该协程会在合适的时候执行println("Hello, world")这个函数。
  1. G 进入 P 的本地队列
  • 新创建的 G 会被放入一个 P 的本地队列中。如果本地队列已满,它可能会被放入全局队列或者其他的本地队列中。
  • 每个 P 会从自己的本地队列中取出 G 来执行。这样可以减少锁的竞争,提高调度效率,因为每个 P 在大部分时间里都可以独立地从自己的本地队列中获取 G 进行处理。
  1. M 与 P 的关联和 G 的执行
  • 当一个 M 空闲时,它会尝试获取一个 P。如果获取到 P,M 会从 P 的本地队列中取出一个 G 来执行。
  • 在执行 G 的过程中,如果 G 执行了阻塞操作(如系统调用、通道操作等),M 可能会将当前的 G 暂停,并尝试从 P 的本地队列中获取下一个 G 来执行。如果 P 的本地队列中没有 G,M 可能会从全局队列或者其他 P 的本地队列中获取 G。
  1. 调度循环
  • M 会不断地从 P 的本地队列或者其他地方获取 G 并执行,这个过程形成一个调度循环。当 G 执行完成后,M 会将 G 标记为已完成,并可能会从本地队列或者其他地方获取下一个 G 继续执行。

三、GMP 调度模型的源码解析


  1. 关键数据结构
  • runtime.h中的定义
  • 在 Go 的源码中,runtime.h文件包含了很多关键的数据结构定义。例如,struct G结构体定义了协程的各种属性,包括栈指针、状态、调度相关的信息等。
  • struct M结构体定义了与操作系统线程对应的信息,如线程 ID、当前正在执行的 G 等。
  • struct P结构体定义了处理器相关的信息,如本地队列的指针、状态等。
  1. 调度函数
  • schedule()函数
  • 这是 Go 运行时调度的核心函数之一。它主要负责选择下一个要执行的 G。它会首先检查当前 P 的本地队列,如果本地队列中有 G,就从中取出一个 G 进行执行。
  • 如果本地队列中没有 G,它会尝试从全局队列中获取 G。在获取 G 的过程中,会涉及到一些锁的操作,以确保全局队列的并发安全。
  • findrunnable()函数
  • 这个函数用于寻找一个可运行的 G。它会综合考虑本地队列、全局队列以及其他可能的来源(如从其他 P 的本地队列中 “窃取” G)来找到一个可以执行的 G。它的实现细节涉及到复杂的队列操作和调度策略。
  1. 协程的创建和初始化源码
  • runtime/proc.go文件中,newproc()函数负责创建新的协程。它会分配一个新的G结构体,初始化其栈空间和相关的参数,然后将其放入合适的队列(通常是 P 的本地队列)中等待执行。
  • 例如,newproc()函数中的部分代码用于设置新协程的栈空间大小和栈顶指针等,确保协程在执行时有足够的空间来存储局部变量和函数调用信息。
  1. 与操作系统线程的交互
  • Go 语言的运行时需要与操作系统线程进行交互。在runtime/os_linux.go(以 Linux 系统为例)等文件中,有代码用于创建和管理操作系统线程。
  • 当一个 M 需要创建一个操作系统线程时,会调用相关的系统调用(如clone()函数)来创建一个新的线程。同时,在这个线程的启动函数中,会调用 Go 运行时的调度函数,使得这个线程能够参与到协程的调度过程中。


通过深入研究 Go 语言的 GMP 调度模型和相关源码,可以更好地理解 Go 语言高效并发处理的机制,并且在开发高性能的并发程序时能够更加得心应手。

相关文章
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
77 2
|
3天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
3天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
3天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
26天前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
54 12
|
22天前
|
PyTorch Shell API
Ascend Extension for PyTorch的源码解析
本文介绍了Ascend对PyTorch代码的适配过程,包括源码下载、编译步骤及常见问题,详细解析了torch-npu编译后的文件结构和三种实现昇腾NPU算子调用的方式:通过torch的register方式、定义算子方式和API重定向映射方式。这对于开发者理解和使用Ascend平台上的PyTorch具有重要指导意义。
|
4天前
|
安全 搜索推荐 数据挖掘
陪玩系统源码开发流程解析,成品陪玩系统源码的优点
我们自主开发的多客陪玩系统源码,整合了市面上主流陪玩APP功能,支持二次开发。该系统适用于线上游戏陪玩、语音视频聊天、心理咨询等场景,提供用户注册管理、陪玩者资料库、预约匹配、实时通讯、支付结算、安全隐私保护、客户服务及数据分析等功能,打造综合性社交平台。随着互联网技术发展,陪玩系统正成为游戏爱好者的新宠,改变游戏体验并带来新的商业模式。
|
3月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
144 4
Golang语言之管道channel快速入门篇
|
3月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
72 4
Golang语言文件操作快速入门篇
|
3月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
119 3
Golang语言之gRPC程序设计示例