【Go并发编程】Goroutine 调度器揭秘:从 GMP 模型到 Work Stealing 算法
每天一篇Go语言干货,从核心到百万并发实战,快来关注魔法小匠,一起探索Go语言的无限可能!
在 Go 语言中,Goroutine 是一种轻量级的并发执行单元,它使得并发编程变得简单高效。而 Goroutine 的高效调度机制是 Go 语言在并发处理上的一大亮点。本文将深入剖析 Go 语言的 Goroutine 调度器,从 GMP 模型到 Work Stealing 算法,带你一探究竟。
一、Goroutine 调度器的背景
Go 语言的并发模型基于 Goroutine,它是一种轻量级的线程,由 Go 运行时(runtime)自动管理。Goroutine 的调度机制决定了多个 Goroutine 如何高效地映射到操作系统线程上执行。与传统线程(Thread)相比具有以下优势:
- 内存占用仅2KB(线程默认1MB)
- 上下文切换成本仅0.2μs(线程约1μs)
- 创建速度达到微秒级(线程需毫秒级)
但是Goroutine本质上是用户态线程,需要依赖GMP调度器将其映射到操作系统线程(M)执行。
二、GMP 模型:Goroutine 调度的核心
Goroutine 的调度基于 GMP 模型,即 Goroutine(G)、Machine(M)和 P(Processor)的组合。这个模型实现了从 N:1(用户态线程到内核态线程)到 N:M(用户态线程到内核态线程的灵活映射)的调度。
1. Goroutine(G)
Goroutine 是用户定义的协程,它代表了并发执行的任务。创建 Goroutine 的底层方法是newproc 函数,它会将 Goroutine 放入 P 的本地队列中。如果本地队列已满,则放入全局队列中。
go func() {// 任务代码
}()
2. Machine(M)
Machine 代表操作系统线程,是 Go 运行时与操作系统交互的接口。Go 运行时会根据需要创建和销毁 M,以适应不同的并发场景。
3. Processor(P)
Processor 是 Go 运行时中的调度上下文,它负责管理 Goroutine 的调度。每个 P 有自己的本地队列,用于存储待执行的 Goroutine。
4.GMP模型示意图

通过该示意图可以了解到完整的GMP模型关系。
全局队列:本地队列(Processor调度器管理)满了的情况下,将会把新创建的Goroutine加入到全局队列中排队等待执行。
本地队列:存放即将执行的Goroutine,每个processor中的goroutine将并行执行。
Goroutine(G):图中的每个圆形图标G就是代表一个Groutine。
Processor(P):管理当前调度器内的本地队列,并负责管理 Goroutine 的调度,用于存储待执行的 Goroutine。processor和groutine是N:M的关系。
内核线程(M):每个M代表了一个内核线程,操作系统调度器负责把内核线程分配到CPU的核上执行。
三、调度器的工作流程
Go 调度器的核心任务是从队列中获取可执行的 Goroutine,并将其分配给可用的 M 执行。
1. 本地队列
每个 P 都有一个本地队列,调度器会优先从本地队列中获取 Goroutine 执行。如果本地队列为空,则会尝试从全局队列获取。
2. 全局队列
全局队列是所有 P 共享的队列,用于存储未被分配的 Goroutine。当本地队列为空时,调度器会尝试从全局队列中获取 Goroutine。
3. Work Stealing(工作窃取)
如果本地队列和全局队列都为空,调度器会采用 Work Stealing 算法,从其他 P 的本地队列中“偷取” Goroutine。这种策略可以实现线程之间的负载均衡。
func runqsteal(pp, p2 *p, stealRunNextG bool) *g {t := pp.runqtailn := runqgrab(p2, &pp.runq, t, stealRunNextG)if n == 0 {return nil}n--gp := pp.runq[(t+n)%uint32(len(pp.runq))].ptr()if n == 0 {return gp}h := atomic.LoadAcq(&pp.runqhead)if t-h+n >= uint32(len(pp.runq)) {throw("runqsteal: runq overflow")}atomic.StoreRel(&pp.runqtail, t+n)return gp
}
四、抢占式调度
Go 调度器采用抢占式调度策略,以防止某个 Goroutine 占用过多 CPU 资源。在 Go 1.14 之后,调度器在任何安全点都可以进行抢占。
func findRunnable() (gp *g, inheritTime, tryWakeP bool) {mp := getg().mtoppp := mp.p.ptr()// 每61次调度周期就检查一次全局G队列if pp.schedtick%61 == 0 && sched.runqsize > 0 {lock(&sched.lock)gp := globrunqget(pp, 1)unlock(&sched.lock)if gp != nil {return gp, false, false}}// 本地队列if gp, inheritTime := runqget(pp); gp != nil {return gp, inheritTime, false}// 全局队列if sched.runqsize != 0 {lock(&sched.lock)gp := globrunqget(pp, 0)unlock(&sched.lock)if gp != nil {return gp, false, false}}// 工作窃取if mp.spinning || 2*sched.nmspinning.Load() < gomaxprocs-sched.npidle.Load() {if !mp.spinning {mp.becomeSpinning()}gp, inheritTime, _, _, _ := stealWork(now)if gp != nil {return gp, inheritTime, false}}return nil, false, false
}
五、协作式调度
除了抢占式调度,Go 还支持协作式调度。Goroutine 可以通过调用runtime.Gosched() 函数主动让出 CPU 的执行权。
func main() {go func() {for i := 0; i < 10; i++ {fmt.Println("Goroutine 1")runtime.Gosched()}}()for i := 0; i < 10; i++ {fmt.Println("Goroutine 2")}
}
六、总结
Go 语言的 Goroutine 调度机制通过 GMP 模型和 Work Stealing 算法实现了高效的并发执行。抢占式调度和协作式调度策略确保了 Goroutine 的公平执行,而 Work Stealing 算法则进一步提高了多核处理器上的负载均衡。通过这些机制,Go 运行时能够高效地利用系统资源,实现高性能的并发编程。
相关文章:
【Go并发编程】Goroutine 调度器揭秘:从 GMP 模型到 Work Stealing 算法
每天一篇Go语言干货,从核心到百万并发实战,快来关注魔法小匠,一起探索Go语言的无限可能! 在 Go 语言中,Goroutine 是一种轻量级的并发执行单元,它使得并发编程变得简单高效。而 Goroutine 的高效调度机制是…...
c# -01新属性-模式匹配、弃元、析构元组和其他类型
文章目录 **学习摘抄分享**模式匹配概述Null 检查类型测试比较离散值关系模型多个输入ObServation列表模式弃元元组和对象析构利用switch的模式进行匹配对于out的方法调用独立弃元析构元组和其他类型元组方法一方法二方法三方法四使用弃元元组的元素使用弃元的用户定义类型解构…...
同步异步日志系统-日志落地模块的实现
功能:将格式化完成后的日志消息字符串,输出到指定的位置 扩展:支持同时将日志落地到不同的位置 位置分类: 1.标准输出 2.指定文件(时候进行日志分析) 3.滚动文件(文件按照时间/大小进行滚动…...
LabVIEW 天然气水合物电声联合探测
天然气水合物被认为是潜在的清洁能源,其储量丰富,预计将在未来能源格局中扮演重要角色。由于其独特的物理化学特性,天然气水合物的探测面临诸多挑战,涉及温度、压力、电学信号、声学信号等多个参数。传统的人工操作方式不仅效率低…...
类型通配符上限
主函数 package typeWildcardTop;import java.util.ArrayList;public class typeWildcardTopTest {/**/public static void main(String[] args) { // test1();test2();}/*测试showList接收ArrayList类型 ArrayList接收各种类型参数创建animals cats mincats集合 传入s…...
嵌入式音视频开发(二)ffmpeg音视频同步
系列文章目录 嵌入式音视频开发(零)移植ffmpeg及推流测试 嵌入式音视频开发(一)ffmpeg框架及内核解析 嵌入式音视频开发(二)ffmpeg音视频同步 嵌入式音视频开发(三)直播协议及编码器…...
Mongodb数据管理
Mongodb数据管理 1.登录数据库,查看默认的库 [rootdb51~]# mongo> show databases; admin 0.000GB config 0.000GB local 0.000GB> use admin switched to db admin > show tables system.version > admin库:admin 是 MongoDB 的管理…...
Django 创建表 choices的妙用:get_<field_name>_display()
1.定义choices 我在创建表时,对于性别这个字段,定义了choices 选项,1代表男,2代表女 mysql中表的数据如下,里面存储的是1或2 如果我们在网页上展示的时候,想展示“男”或“女”,而不是数字1或2…...
Spring Boot 集成 Kettle
Kettle 简介 Kettle 最初由 Matt Casters 开发,是 Pentaho 数据集成平台的一部分。它提供了一个用户友好的界面和丰富的功能集,使用户能够轻松地设计、执行和监控 ETL 任务。Kettle 通过其强大的功能和灵活性,帮助企业高效地处理大规模数据集…...
自学Java-面向对象高级(final、单例类、枚举类、抽象类、接口)
自学Java-面向对象高级(final、单例类、枚举类、抽象类、接口) 一、final关键字1、认识final关键字2、final修饰变量的注意3、常量 二、单例类(设计模式)1、设计模式的概念2、单例设计模式3、单例类有很多形式4、懒汉式单例类5、小…...
Hutool - Cache:简单而强大的缓存实现
目录 1. 缓存简介 2. 引入依赖 3. 常见缓存类型及使用示例 3.1 FIFO 缓存(先进先出缓存) 3.2 LRU 缓存(最近最少使用缓存) 3.3 定时缓存 4. 缓存的基本操作 5. 总结 1. 缓存简介 在软件开发中,缓存是一种常用的…...
DeepSeek 通过 API 对接第三方客户端 告别“服务器繁忙”
本文首发于只抄博客,欢迎点击原文链接了解更多内容。 前言 上一期分享了如何在本地部署 DeepSeek R1 模型,但通过命令行运行的本地模型,问答的交互也要使用命令行,体验并不是很好。这期分享几个第三方客户端,涵盖了桌…...
Python 基础-循环
目录 简介 break continue 小结 简介 要计算123,我们可以直接写表达式: >>> 1 2 3 6要计算123...10,勉强也能写出来。 但是,要计算123...10000,直接写表达式就不可能了。 为了让计算机能计算成千上…...
Java和SQL测试、性能监控中常用工具
下面我会详细列举一些在Java和SQL测试、调试、性能监控中常用的工具,并结合项目中提到的各个技术点说明如何选择合适的工具和方法。 一、Java项目常用的测试、调试与性能监控工具 单元测试与集成测试:JUnit/TestNG: 用于编写单元测试和集成测…...
SQL 注入攻击详解[基础篇]:Web 应用程序安全漏洞与防御策略
目录 SQL注入的简介 现代 Web 应用程序中的数据库交互与 SQL 注入攻击 数据库管理系统(DBMS)架构与 SQL 注入 什么是 SQL 注入? SQL 注入的工作原理 SQL 注入的用例与影响 如何预防 SQL 注入? 数据库分类 数据库类型&am…...
【ArcGIS Pro二次开发】(87):样式_Style的用法
.Stylx类型的文件即为样式库文件,保存了符号样式。 1、根据名字获取当前工程中的style //获取当前工程中的所有style var ProjectStyles Project.Current.GetItems<StyleProjectItem>();//根据名字找出指定的style StyleProjectItem style ProjectStyles.F…...
DEX-EE三指灵巧手:扩展AI与机器人研究的边界
DEX-EE三指灵巧手,由Shadow Robot与Google DeepMind合作开发,以其先进技术和设计,正在引领AI与机器人研究的新趋势。其高精度传感器和灵活的机械手指,能够捕捉复杂的环境数据,为强化学习实验提供了可靠支持。 Shadow R…...
简站主题:简洁、实用、SEO友好、安全性高和后期易于维护的wordpress主题
简站主题以其简洁的设计风格、实用的功能、优化的SEO性能和高安全性而受到广泛好评。 简洁:简站主题采用扁平化设计风格,界面简洁明了,提供多种布局和颜色方案,适合各种类型的网站,如个人博客和企业网站。 实用&…...
23种设计模式 - 责任链
模式定义 责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,允许多个对象按链式顺序处理请求,直到其中一个对象处理为止。该模式将请求的发送者和接收者解耦,使多个对象都有机会处理请求。 模式结构…...
Flink SQL与Doris实时数仓Join实战教程(理论+实例保姆级教程)
目录 第一章:Regular Joins 深度解析 1.1 核心原理与适用场景 1.2 电商订单 - 商品实时关联案例 1.2.1 数据流设计 1.2.2 Doris 表设计优化 1.2.3 性能调优要点 第二章:Interval Joins 实战应用 2.1 时间区间关联原理 2.2 优惠券使用有效性验证 2.2.1 业务场景说明 …...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
