Golang 中 Goroutine 的调度

Golang 中 Goroutine 的调度

Golang 中的 Goroutine 是一种轻量级的线程,由 Go 运行时(runtime)自动管理。Goroutine 的调度基于 M:N 模型,即多个 Goroutine 可以映射到多个操作系统线程上执行。以下是详细的调度过程和策略:

1. 创建 Goroutine
  • 使用 go 关键字可以创建一个新的 Goroutine。例如:
  go func() {// 任务代码}()
  • 创建 Goroutine 的底层方法是 newproc 函数,该函数会创建一个新的 Goroutine 并将其放入 P 的本地队列中。如果本地队列已满,则放入全局队列中。
2. 调度器(Scheduler)
  • Go 运行时包含一个调度器,负责管理 Goroutine 的执行。调度器的主要任务是从全局队列或本地队列获取可执行的 Goroutine,并将其分配给可用的线程(M)执行。
  • 调度器采用队列轮转法,确保每个 Goroutine 都有机会被执行。具体来说,调度器会从 P 的本地队列中获取 Goroutine 执行;如果本地队列为空,则从全局队列获取;如果全局队列也为空,则从其他 P 的本地队列中“偷取”一半数量的 Goroutine(称为 work stealing)。
3. Goroutine 的状态
  • Goroutine 可能处于多种状态,包括 _Grunning(正在运行)、_Gwaiting(等待中)、_Gblocked(阻塞中)等。
  • 当一个 Goroutine 完成其任务后,会调用 goexit 函数,该函数会将当前 Goroutine 放入 P 的复用链表中,并调用 schedule() 函数继续调度下一个 Goroutine。
4. 抢占式调度
  • Go 调度器是抢占式的,每个 Goroutine 最多执行 10ms 后会被替换。如果 Goroutine 运行超过 10ms,调度器会设置“抢占标志位”,但这一机制仅在有函数调用的情况下生效。
  • 当 Goroutine 发生阻塞(如等待通道、垃圾回收、sleep 休眠、锁等待、IO 阻塞等),调度器会将当前 Goroutine 调度走,让其他 Goroutine 来执行。
5. 调度时机
  • 调度时机包括但不限于:Goroutine 完成任务、发生阻塞、系统调用、垃圾回收等。
  • sysmon 是一个专门用于监控和管理 Goroutine 的线程,它记录所有 P 的 G 任务数量,并使用 schedtick 变量进行计数。如果 schedtick 一直没有递增,说明该 P 一直在执行同一个任务;如果持续超过 10ms,则会在该 G 任务的栈信息上加一个标记,G 任务在执行时检查此标记并中断自己,将自己添加到队列末尾,等待下一个 G 任务执行。
6. 核心数据结构
  • Go 运行时维护了三个核心数据结构:G(Goroutine)、P(Processor)、M(Machine)。
    • G 表示 Goroutine,每个 Goroutine 对应一个 G 结构体,存储其运行堆栈、状态和任务函数。
    • P 表示 Processor,相当于 CPU 核,为 G 提供执行环境。
    • M 是 OS 线程抽象,负责调度任务,代表真正执行计算的资源。
7. 调度流程
  • 当一个程序启动时,只有一个主 Goroutine 来调用 main 函数。在运行过程中,可以通过 go 关键字创建新的 Goroutine。
  • M 需要绑定一个 P 才能被调度执行,并在绑定后进入 schedule 循环,从全局队列或 P 的本地队列获取 Goroutine 并执行。
  • 当 M 执行完一个 Goroutine 后,会调用 goexitgoexit1 函数,保存当前 Goroutine 的上下文,切换到 g0 及其栈,调用传入的方法。最后,goexit0 函数清零 Goroutine 属性,状态从 _Grunning 改为 _Gdead,解绑 M 和 Goroutine,放入队列,重新调度。

总结

Golang 的 Goroutine 调度机制通过 M:N 模型实现了高效的并发执行。调度器负责管理 Goroutine 的生命周期和执行顺序,确保每个 Goroutine 都有机会被执行。通过抢占式调度和 work stealing 策略,Go 运行时能够高效地利用系统资源,实现高性能的并发编程。

Goroutine 和 OS 线程之间的映射机制是如何工作的?

Goroutine 和 OS 线程之间的映射机制主要通过 Go 运行时的调度器实现,采用 M:N 模型。这种模型允许将多个 goroutine 映射到较少数量的 OS 线程上,从而提高并发执行的效率。

具体来说,Go 运行时内部包含三个关键结构:M(Machine,即 OS 线程)、G(Goroutine)和 P(调度上下文)。M 代表真正的内核线程,G 代表用户态定义的协程,而 P 则负责调度,实现从 N:1 到 N:M 的用户空间线程与内核空间线程的映射。

在 Go 程序启动时,会创建一个或多个 OS 线程,并在这些线程上调度执行 goroutine。当一个 goroutine 需要执行时,Go 运行时会从线程池中选出一个可用的 M 或者新建一个 M。当一个 goroutine 阻塞(如进行 I/O 操作)时,Go 运行时可以将 P 转移到另一个 M 上继续执行其他 goroutine,从而提高 CPU 的利用率。

此外,Go 运行时还引入了 GOMAXPROCS 配置参数,它决定了 Go 代码可以同时执行的 OS 线程数量。通过调整该参数,开发者可以优化应用性能,但需平衡过高设置可能导致的资源竞争和上下文切换开销增加。

Go 调度器在不同操作系统(如 Linux 和 Windows)上的实现差异有哪些?

Go 调度器在不同操作系统(如 Linux 和 Windows)上的实现存在一些差异,主要体现在 I/O 多路复用机制和调度策略上。

  1. I/O 多路复用机制

    • Linux:Go 语言的运行时调度器在 Linux 上使用了 epoll 机制来实现 I/O 多路复用。epoll 是一种高效的事件驱动机制,能够处理大量并发的网络连接和 I/O 操作,从而提高系统的性能和响应速度。
    • Windows:在 Windows 上,Go 语言的运行时调度器使用了 IOCP(I/O Completion Port)机制。IOCP 是 Windows 特有的事件驱动机制,同样能够高效地处理大量并发的 I/O 操作。
  2. 调度策略

    • Linux:Linux 的进程调度器将线程作为最小调度单位,通过调度类的概念引入不同的调度策略来平衡低延时和实时性的问题。
    • Go 调度器:Go 调度器在用户层建立了新的模型,以 Goroutine 作为最小调度单位。这种设计使得 Go 调度器能够在用户态进行调度,减少了操作系统线程调度和上下文切换的开销。此外,Go 调度器还采用了工作窃取(Work Stealing)的方式,通过运行队列进行分区,平衡不同 CPU 或线程上的运行队列。
  3. 系统调用处理

    • Linux:Linux 系统调用通常由内核直接处理,而 Go 调度器需要通过用户态的机制来处理这些系统调用,例如使用 epoll 来处理网络请求和 I/O 操作。
    • Windows:在 Windows 上,Go 调度器使用 IOCP 来处理系统调用,这同样是一种高效的事件驱动机制,能够有效地处理大量并发的 I/O 操作。
  4. 调度器架构的演变

    • 早期 Go 调度器:最早期的 Go 调度器(Go 1 之前)甚至不能很好地支持多线程,最大 M 数为 1。这个版本的调度器在 Linux 上实现,但在其他操作系统上的支持并不完善。
    • 现代 Go 调度器:从 Go 1.1 起,引入了工作窃取调度器,大大提高了调度效率和并发性能。现代 Go 调度器在不同操作系统上都进行了优化,以适应各自的 I/O 多路复用机制和调度策略。

综上所述,Go 调度器在不同操作系统上的实现差异主要体现在 I/O 多路复用机制和调度策略上。Linux 上使用 epoll,而 Windows 上使用 IOCP

如何优化 Go 程序中的 Goroutine 使用以提高性能?

优化 Go 程序中的 Goroutine 使用以提高性能,可以从以下几个方面进行:

  1. 合理控制 Goroutine 数量

    • 过多的 Goroutine 会导致系统资源的过度消耗,甚至引发 Goroutine 泄露问题。因此,应根据实际需求合理控制 Goroutine 的数量,避免过度并发。
    • 使用 Goroutine 池化技术可以减少 Goroutine 的创建和销毁开销,从而提高性能。
  2. 优化 Channel 的使用

    • Channel 是 Goroutine 之间通信的主要方式,但 Channel 的传递大数据会带来值拷贝的开销。因此,尽量避免在 Channel 中传递大数据。
    • 使用 Channel 时,应尽量避免阻塞和死锁问题,确保 Channel 的使用高效且安全。
  3. 减少锁的使用

    • 锁是并发编程中常见的同步机制,但过度使用锁会导致性能下降。Go 推荐使用 Channel 的方式调用而不是共享内存,因为 Channel 之间存在大锁,可以降低锁的竞争力度。
    • 无锁编程通过原子操作减少锁的使用,可以进一步提升并发性能。
  4. 使用 Context 控制 Goroutine 生命周期

    • 设置超时和使用 context.WithTimeout 可以有效管理 Goroutine 的生命周期,避免 Goroutine 泄露。
  5. 减少系统调用

    • Goroutine 的实现是通过同步模拟异步操作,建议将同步调用隔离到可控 Goroutine 中,而不是直接高并发调用。
  6. 使用性能分析工具

    • 使用 Go 提供的性能分析工具如 pprof 和 trace,可以帮助检测 Goroutine 的运行时间、资源占用等,从而对 Goroutine 的创建和管理进行优化。
  7. 合理设置 GOMAXPROCS

    • GOMAXPROCS 控制了 Go 运行时可以使用的最大工作线程数。合理设置 GOMAXPROCS 可以提高并发性能。
  8. 避免内存泄漏

    • 在使用切片和映射时要合理设置容量,避免内存泄漏。
Goroutine 的抢占式调度机制具体是如何实现的?

Goroutine 的抢占式调度机制在 Go 语言中是通过多种方式实现的,主要包括基于协作的抢占和基于信号的抢占。以下是详细的实现机制:

  1. 基于协作的抢占

    • Goroutine 栈保护:每个 Goroutine 都有一个 stackguard0 字段,当该字段被设置为 StackPreempt 时,Goroutine 将被抢占。这个机制确保在函数调用时,调度器可以检查并触发抢占。
    • 抢占函数:Go 运行时引入了 preemptonepreemptall 函数,这些函数会设置 Goroutine 的 StackPreempt 标志,从而触发抢占。
    • 垃圾回收抢占:在垃圾回收阶段,运行时会调用 preemptall 函数设置所有处理器上 Goroutine 的 StackPreempt 标志,以确保垃圾回收期间所有 Goroutine 都能被抢占。
    • 长时间运行的 Goroutine 抢占:如果一个 Goroutine 的运行时间超过 10ms,系统监控线程(sysmon)会调用 retakepreemptone 函数进行抢占。
  2. 基于信号的抢占

    • 信号处理:在 Go 1.14 版本中,引入了基于信号的抢占机制。当系统处于特定状态(如 STW、执行 safe point 函数、sysmon 监控期间等)时,会向线程发送 SIGURG 信号。
    • 抢占处理函数:当线程收到 SIGURG 信号后,会调用 asyncPreempt 函数,将 asyncPreempt 的调用强制插入到用户当前执行的代码位置,从而实现真正的抢占。
  3. 系统调用引起的抢占

    • sysmon 线程监控:sysmon 线程负责监控系统资源和调度 Goroutine。当发现某个 Goroutine 执行系统调用时间过长时,会调用 retake 函数进行抢占。
    • 抢占逻辑:在抢占过程中,sysmon 会释放系统调用线程所绑定的 P(进程),而非阻止线程进行系统调用,而是合理利用资源。
  4. 调度器的抢占流程

    • 抢占时机:抢占式调度在以下场景下触发:Goroutine 执行时间过长、执行较长的函数调用链或栈帧扩展时。
    • 抢占行为:当 Goroutine 被标记为需要抢占时,调度器会将其放入全局可运行队列中,并更新其状态为 _Runnable,然后继续调度其他 Goroutine。

通过上述机制,Go 调度器能够确保在大部分情况下,不同的 Goroutine 都能获得均匀的时间片,提高了程序的可靠性和稳定性。

在高并发场景下,Goroutine 的调度策略对系统资源的利用效率有何影响?

在高并发场景下,Goroutine 的调度策略对系统资源的利用效率有显著影响。以下是详细的分析:

  1. 抢占式调度:Go 运行时系统实现了抢占式调度,以确保 Goroutine 公平地获得执行时间。如果一个 Goroutine 长时间占用 CPU(大约 10ms),运行时系统会抢占它,将其放回运行队列,并允许其他 Goroutine 执行。这种机制可以防止某个 Goroutine 占用过多资源,从而提高整体系统的资源利用率。

  2. 工作窃取算法:当一个 P(逻辑处理器)的本地运行队列为空时,它会尝试从其他 P 的本地运行队列中“偷取” Goroutine,以保持 CPU 的利用率。这种工作窃取算法避免了全局锁的使用,提高了调度效率,特别是在多核处理器上,可以更好地平衡负载。

  3. 优先级调度:Goroutine 的优先级是由 Go 运行时根据任务的优先级来设置的。优先级高的 Goroutine 会得到更多的资源分配,因此可以更快地执行。然而,在一些高并发场景下,大量请求 Goroutine 和其他工作 Goroutine 数量级差别较大,导致调度不合理,有时会导致调度不均衡。

  4. 资源分配:Goroutine 的资源分配是由 Go 运行时根据任务的资源需求来设置的,包括 CPU 时间片和内存空间等。在高并发场景下,频繁创建和销毁 Goroutine 可能会导致垃圾回收(GC)负担加重,影响系统响应速度。

  5. 锁竞争和优化:高频繁的锁竞争会导致性能瓶颈,特别是在高并发环境下。减少全局锁的使用和优化锁的策略可以提高系统的性能。

  6. 用户态调度:Goroutine 的调度在用户态进行,避免了内核态的资源争用和线程管理问题。这种机制使得 Goroutine 的切换更加高效,因为它们不依赖于操作系统提供的线程,而是通过用户态的切换实现。

  7. GOMAXPROCS 参数:GOMAXPROCS 参数决定了同时可执行的 Goroutine 数量,默认值为 CPU 核心数。调整 GOMAXPROCS 参数可以影响调度行为,从而优化资源利用效率。

  8. 内存管理:Goroutine 的栈大小是动态调整的,从 2KB 开始,最大可达 1GB。这种动态调整机制使得 Goroutine 能够更高效地利用内存资源,避免了固定大小栈带来的浪费。

综上所述,Goroutine 的调度策略在高并发场景下对系统资源的利用效率有显著影响。通过合理的调度策略和优化方法,可以提高系统的性能和资源利用率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/64609.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

clickhouse-backup配置及使用(Linux)

一、下载地址 Releases Altinity/clickhouse-backup GitHub 二、上传到服务器解压安装 自行上传至服务器,解压命令: tar xvf clickhouse-backup-linux-amd64.tar.gz 三、创建软连接 sudo ln -sv build/linux/amd64/clickhouse-backup /usr/local/bin/…

如何在群晖NAS上安装并配置MySQL与phpMyAdmin远程管理数据库

文章目录 前言1. 安装MySQL2. 安装phpMyAdmin3. 修改User表4. 本地测试连接MySQL5. 安装cpolar内网穿透6. 配置MySQL公网访问地址7. 配置MySQL固定公网地址8. 配置phpMyAdmin公网地址9. 配置phpmyadmin固定公网地址 前言 大家是不是经常遇到需要随时随地访问自己数据的情况&am…

《向量数据库指南》——Milvus Cloud 2.5:Sparse-BM25引领全文检索新时代

Milvus Cloud BM25:重塑全文检索的未来 在最新的Milvus Cloud 2.5版本中,我们自豪地引入了“全新”的全文检索能力,这一创新不仅巩固了Milvus Cloud在向量数据库领域的领先地位,更为用户提供了前所未有的灵活性和效率。作为大禹智库的向量数据库高级研究员,以及《向量数据…

SQL 总结

SQL 总结 引言 SQL(Structured Query Language,结构化查询语言)是一种用于管理关系数据库管理系统(RDBMS)的标准编程语言。自1974年首次提出以来,SQL已成为数据库领域中不可或缺的一部分。它允许用户执行各种操作,如查询、更新、插入和删除数据库中的数据。本文旨在提…

ESP32-CAM开发板入门 (下载示例程序)

ESP32-CAM开发板例程使用 1、准备工作1.1、硬件准备1.2、软件准备 2、选择示例程序并录入第一步 1、准备工作 1.1、硬件准备 1.2、软件准备 Arduino IDE : 编程与写入(下载地址 https://www.arduino.cc/en/software) 安装好后将软件设置到…

企业赋能是什么意思-国际数字影像产业园解读

在当今竞争激烈的商业环境中,企业赋能已成为推动企业发展、提升竞争力的关键策略。国际数字影像产业园作为数字影像产业的重要集聚地,通过一系列创新举措为入驻园区的我众多企业赋能。那么,企业赋能究竟是什么意思呢? 企业赋能是…

混合并行训练框架性能对比

混合并行训练框架性能对比 1. 框架类型 DeepSpeed、Megatron - LM、Colossal - AI、SageMaker、Merak、FasterMoE、Tutel、Whale、Alpa、DAPPLE、Mesh - TensorFlow 2. 可用并行性(Available parallelisms) DNN framework(深度神经网络框架)DP(数据并行,Data Parallelis…

客户案例:基于慧集通集成平台,打通屠宰管理系统与用友U8C 系统的全攻略

一、引言 本原型客户成立于2014年,是一家集饲草种植、肉牛养殖、精深加工、冷链物流、餐饮服务于一体的大型农牧综合体。公司下设三个子公司分别涵盖农业、畜牧业、肉制品加工业与餐饮物流服务业。公司严格按照一二三产业融合发展要求,以肉牛产业化为支…

HTML5滑块(Slider)

HTML5 的滑块&#xff08;Slider&#xff09;控件允许用户通过拖动滑块来选择数值。以下是如何实现一个简单的滑块组件的详细说明。 HTML5 滑块组件 1. 基本结构 使用 <input type"range"> 元素可以创建一个滑块。下面是基本实现的代码示例&#xff1a; <…

25. C++继承 1 (继承的概念与基础使用, 继承的复制兼容规则,继承的作用域)

⭐上篇模板文章&#xff1a;24. C模板 2 (非类型模板参数&#xff0c;模板的特化与模板的分离编译)-CSDN博客 ⭐本篇代码&#xff1a;c学习 橘子真甜/c-learning-of-yzc - 码云 - 开源中国 (gitee.com) ⭐标⭐是比较重要的部分 目录 一. 继承的基础使用 1.1 继承的格式 1.2 …

露营小程序搭建有哪些步骤?小程序里面可以找个露营搭子

露营不仅仅是走进大自然的旅程&#xff0c;它也成为了一种社交和体验式的活动。随着小程序的普及&#xff0c;露营活动也越来越多地开始在线上开展。通过搭建一个露营小程序&#xff0c;商家不仅可以为用户提供更多的露营选择&#xff0c;还可以帮助他们找到合适的露营搭子。那…

XIAO ESP32 S3网络摄像头——2视频获取

本文主要是使用XIAO Esp32 S3制作网络摄像头的第2步,获取摄像头图像。 1、效果如下: 2、所需硬件 3、代码实现 3.1硬件代码: #include "WiFi.h" #include "WiFiClient.h" #include "esp_camera.h" #include "camera_pins.h"// 设…

记一次 dockerfile 的循环依赖错误

文章目录 1. 写在最前面1.1 具体循环依赖的例子 2. 报错的位置2.1 代码快速分析2.2 代码总结2.3 关于 parser 的记录 3. 碎碎念 1. 写在最前面 笔者在使用 dockerfile 多阶段构建的功能时&#xff0c;写出了一个「circular dependency detected on stage: xx」的错误。 解决方…

AAAI 2025论文分享┆一种接近全监督的无训练文档信息抽取方法:SAIL(文中附代码链接)

本推文详细介绍了一篇上海交通大学乐心怡老师课题组被人工智能顶级会议AAAI 2025录用的的最新论文《SAIL: Sample-Centric In-Context Learning for Document Information Extraction》。论文的第一作者为张金钰。该论文提出了一种无需训练的、以样本为中心的、基于上下文学习的…

小程序信息收集(小迪网络安全笔记~

免责声明&#xff1a;本文章仅用于交流学习&#xff0c;因文章内容而产生的任何违法&未授权行为&#xff0c;与文章作者无关&#xff01;&#xff01;&#xff01; 附&#xff1a;完整笔记目录~ ps&#xff1a;本人小白&#xff0c;笔记均在个人理解基础上整理&#xff0c;…

pat 乙级1096 大美数

若正整数 N 可以整除它的 4 个不同正因数之和&#xff0c;则称这样的正整数为“大美数”。本题就要求你判断任一给定的正整数是否是“大美数”。 输入格式&#xff1a; 输入在第一行中给出正整数 K&#xff08;≤10&#xff09;&#xff0c;随后一行给出 K 个待检测的、不超过…

C#封送类

封送类&#xff08;Marshaling classes&#xff09;在.NET框架中扮演着至关重要的角色&#xff0c;尤其是在托管代码与非托管代码之间进行数据交换时。封送过程涉及到将托管环境中的对象转换为非托管环境中可以理解的形式&#xff0c;并且反之亦然。这一过程确保了两种不同类型…

计算机体系结构期末考试

1、描述计算机系统性能评估的关键指标&#xff0c;并以SPEC CPU benchmark为例&#xff0c;讨论如何使用几何平均数与加权平均数对性能进行量化。此外&#xff0c;描述Amdahl定律并分析该定律的应用场景及其对性能优化的局限性 2、请对比RISC和CISC指令集架构的设计思想及优缺点…

药片缺陷检测数据集,8625张图片,使用YOLO,PASICAL VOC XML,COCO JSON格式标注,可识别药品是否有缺陷,是否完整

药片缺陷检测数据集&#xff0c;8625张图片&#xff0c;使用YOLO&#xff0c;PASICAL VOC XML&#xff0c;COCO JSON格式标注&#xff0c;可识别药品是否有缺陷&#xff0c;是否完整 有缺陷的标注信息&#xff1a; 无缺陷的标注信息 数据集下载&#xff1a; yolov11:https://d…

一文讲清楚CSS3新特性

文章目录 一文讲清楚CSS3新特性1. 新增选择器特性2. 新增的样式3. 新增布局方式 一文讲清楚CSS3新特性 1. 新增选择器特性 层次选择器(div~p)选择前面有div的p元素伪类选择器 :first-of-type 表示⼀组同级元素中其类型的第⼀个元素:last-of-type 表示⼀组同级元素中其类型的最…