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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 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 语言高效并发处理的机制,并且在开发高性能的并发程序时能够更加得心应手。

相关文章
|
14天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
45 2
|
23天前
|
存储 安全 测试技术
GoLang协程Goroutiney原理与GMP模型详解
本文详细介绍了Go语言中的Goroutine及其背后的GMP模型。Goroutine是Go语言中的一种轻量级线程,由Go运行时管理,支持高效的并发编程。文章讲解了Goroutine的创建、调度、上下文切换和栈管理等核心机制,并通过示例代码展示了如何使用Goroutine。GMP模型(Goroutine、Processor、Machine)是Go运行时调度Goroutine的基础,通过合理的调度策略,实现了高并发和高性能的程序执行。
78 29
|
14天前
|
存储 网络协议 安全
30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场
本文精选了 30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场。
47 2
|
21天前
|
负载均衡 算法 Go
GoLang协程Goroutiney原理与GMP模型详解
【11月更文挑战第4天】Goroutine 是 Go 语言中的轻量级线程,由 Go 运行时管理,创建和销毁开销小,适合高并发场景。其调度采用非抢占式和协作式多任务处理结合的方式。GMP 模型包括 G(Goroutine)、M(系统线程)和 P(逻辑处理器),通过工作窃取算法实现负载均衡,确保高效利用系统资源。
|
24天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
63 4
|
21天前
|
安全 测试技术 Go
Go语言中的并发编程模型解析####
在当今的软件开发领域,高效的并发处理能力是提升系统性能的关键。本文深入探讨了Go语言独特的并发编程模型——goroutines和channels,通过实例解析其工作原理、优势及最佳实践,旨在为开发者提供实用的Go语言并发编程指南。 ####
|
3月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
121 4
Golang语言之管道channel快速入门篇
|
3月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
67 4
Golang语言文件操作快速入门篇
|
3月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
106 3
Golang语言之gRPC程序设计示例
|
3月前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
89 4