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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 【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机制。
71 2
|
17天前
|
机器学习/深度学习 人工智能 PyTorch
Transformer模型变长序列优化:解析PyTorch上的FlashAttention2与xFormers
本文探讨了Transformer模型中变长输入序列的优化策略,旨在解决深度学习中常见的计算效率问题。文章首先介绍了批处理变长输入的技术挑战,特别是填充方法导致的资源浪费。随后,提出了多种优化技术,包括动态填充、PyTorch NestedTensors、FlashAttention2和XFormers的memory_efficient_attention。这些技术通过减少冗余计算、优化内存管理和改进计算模式,显著提升了模型的性能。实验结果显示,使用FlashAttention2和无填充策略的组合可以将步骤时间减少至323毫秒,相比未优化版本提升了约2.5倍。
34 3
Transformer模型变长序列优化:解析PyTorch上的FlashAttention2与xFormers
|
15天前
|
PyTorch Shell API
Ascend Extension for PyTorch的源码解析
本文介绍了Ascend对PyTorch代码的适配过程,包括源码下载、编译步骤及常见问题,详细解析了torch-npu编译后的文件结构和三种实现昇腾NPU算子调用的方式:通过torch的register方式、定义算子方式和API重定向映射方式。这对于开发者理解和使用Ascend平台上的PyTorch具有重要指导意义。
|
19天前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
50 12
|
23天前
|
机器学习/深度学习 人工智能 自然语言处理
探索深度学习与自然语言处理的前沿技术:Transformer模型的深度解析
探索深度学习与自然语言处理的前沿技术:Transformer模型的深度解析
72 0
|
3月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
140 4
Golang语言之管道channel快速入门篇
|
3月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
71 4
Golang语言文件操作快速入门篇
|
3月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
112 3
Golang语言之gRPC程序设计示例
|
3月前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
100 4
|
3月前
|
Go 调度
Golang语言goroutine协程篇
这篇文章是关于Go语言goroutine协程的详细教程,涵盖了并发编程的常见术语、goroutine的创建和调度、使用sync.WaitGroup控制协程退出以及如何通过GOMAXPROCS设置程序并发时占用的CPU逻辑核心数。
72 4
Golang语言goroutine协程篇

推荐镜像

更多