热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

探索由一个问题引出的Go语言Goroutine调度机制

本文以Go语言1.13.x版本为基础,探讨了一位开发者在Go语言中文社区提出的一个关于Goroutine输出顺序的问题,深入分析了其背后涉及的Goroutine调度原理。

本文以 Go 语言 1.13.x 版本为背景,探讨了一个来自 Go 语言中文社区的有趣问题。该问题围绕一段特定的代码展开,这段代码展示了如何使用 Goroutine 来并行执行任务,但结果却与预期不符。

具体代码如下:

const N = 26
func main() {
const GOMAXPROCS = 1
runtime.GOMAXPROCS(GOMAXPROCS)
var wg sync.WaitGroup
wg.Add(2 * N)
for i := 0; i go func(i int) {
defer wg.Done()
fmt.Printf("%c", 'a'+i)
}(i)
go func(i int) {
defer wg.Done()
fmt.Printf("%c", 'A'+i)
}(i)
}
wg.Wait()
}

问题的核心在于,为什么输出结果是 'ZaAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYz' 而不是 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ'? 为了得到预期的输出,应该如何调整代码?

这个问题实际上触及到了 Go 语言中 Goroutine 的调度机制。首先需要明确的是,在实际开发中不应依赖于 Goroutine 的调度顺序。然而,从学习的角度出发,我们可以深入探讨这个问题。

一种解决方法是在 wg.Wait() 前开启一个空的 Goroutine,如 go func() {}()。另一种方法则是在 wg.Wait() 前添加一个 time.Sleep(1e9) 调用。这两种方法都能改变输出顺序,使其符合预期。

为了更好地理解 Goroutine 的调度顺序,我们可以通过一个简化的例子来进行实验。例如,设置 runtime.GOMAXPROCS(1) 并观察不同 Goroutine 的执行顺序。通过这些实验,我们可以初步推测 Goroutine 的调度可能是基于后进先出的原则,但也存在例外情况。

进一步地,通过查看 Go 语言的源码,特别是 runtime 包中的相关实现,我们可以了解到 Goroutine 的调度机制。Go 语言采用 GMP(Goroutine, M: Machine, P: Processor)模型,其中每个 P 都维护了一个本地的可运行 Goroutine 队列。当创建一个新的 Goroutine 时,它会被添加到这个队列中,具体的添加位置取决于 runqput 函数的参数。

在深入源码的过程中,我们发现 runqput 函数的关键参数 next 决定了新创建的 Goroutine 是否会被优先执行。如果 nexttrue,新的 Goroutine 将被放置在 runnext 位置,这意味着它将在当前 Goroutine 仍有剩余执行时间的情况下优先执行。

此外,time.Sleep 的实现也为我们提供了一些线索。当首次调用 time.Sleep 时,会启动一个全局的定时器管理 Goroutine,这会影响后续 Goroutine 的调度顺序。

总之,虽然我们不应该依赖于 Goroutine 的具体调度顺序,但了解其背后的机制有助于我们更好地编写高效、可靠的并发程序。希望本文能激发读者对 Go 语言调度机制的兴趣,并鼓励大家深入研究相关源码。


推荐阅读
  • 题目Link题目学习link1题目学习link2题目学习link3%%%受益匪浅!-----&# ... [详细]
  • 火星商店问题:线段树分治与持久化Trie树的应用
    本题涉及编号为1至n的火星商店,每个商店有一个永久商品价值v。操作包括每天在指定商店增加一个新商品,以及查询某段时间内某些商店中所有商品(含永久商品)与给定密码值的最大异或结果。通过线段树分治和持久化Trie树来高效解决此问题。 ... [详细]
  • 题目描述:给定n个半开区间[a, b),要求使用两个互不重叠的记录器,求最多可以记录多少个区间。解决方案采用贪心算法,通过排序和遍历实现最优解。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 扫描线三巨头 hdu1928hdu 1255  hdu 1542 [POJ 1151]
    学习链接:http:blog.csdn.netlwt36articledetails48908031学习扫描线主要学习的是一种扫描的思想,后期可以求解很 ... [详细]
  • 本文详细介绍了 Apache Jena 库中的 Txn.executeWrite 方法,通过多个实际代码示例展示了其在不同场景下的应用,帮助开发者更好地理解和使用该方法。 ... [详细]
  • 本文详细探讨了VxWorks操作系统中双向链表和环形缓冲区的实现原理及使用方法,通过具体示例代码加深理解。 ... [详细]
  • Linux设备驱动程序:异步时间操作与调度机制
    本文介绍了Linux内核中的几种异步延迟操作方法,包括内核定时器、tasklet机制和工作队列。这些机制允许在未来的某个时间点执行任务,而无需阻塞当前线程,从而提高系统的响应性和效率。 ... [详细]
  • Codeforces Round #566 (Div. 2) A~F个人题解
    Dashboard-CodeforcesRound#566(Div.2)-CodeforcesA.FillingShapes题意:给你一个的表格,你 ... [详细]
  • 毕业设计:基于机器学习与深度学习的垃圾邮件(短信)分类算法实现
    本文详细介绍了如何使用机器学习和深度学习技术对垃圾邮件和短信进行分类。内容涵盖从数据集介绍、预处理、特征提取到模型训练与评估的完整流程,并提供了具体的代码示例和实验结果。 ... [详细]
  • 在多线程编程环境中,线程之间共享全局变量可能导致数据竞争和不一致性。为了解决这一问题,Linux提供了线程局部存储(TLS),使每个线程可以拥有独立的变量副本,确保线程间的数据隔离与安全。 ... [详细]
  • 实体映射最强工具类:MapStruct真香 ... [详细]
  • 本次考试于2016年10月25日上午7:50至11:15举行,主要涉及数学专题,特别是斐波那契数列的性质及其在编程中的应用。本文将详细解析考试中的题目,并提供解题思路和代码实现。 ... [详细]
  • 在 Flutter 开发过程中,开发者经常会遇到 Widget 构造函数中的可选参数 Key。对于初学者来说,理解 Key 的作用和使用场景可能是一个挑战。本文将详细探讨 Key 的概念及其应用场景,并通过实例帮助你更好地掌握这一重要工具。 ... [详细]
  • 深入解析TCP/IP五层协议
    本文详细介绍了TCP/IP五层协议模型,包括物理层、数据链路层、网络层、传输层和应用层。每层的功能及其相互关系将被逐一解释,帮助读者理解互联网通信的原理。此外,还特别讨论了UDP和TCP协议的特点以及三次握手、四次挥手的过程。 ... [详细]
author-avatar
爱上小胸女人
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有