首页 > 后端开发 > Golang > 正文

Go 调度器:奇偶循环次数导致 Goroutine 执行差异的探究

碧海醫心
发布: 2025-10-16 11:53:17
原创
395人浏览过

go 调度器:奇偶循环次数导致 goroutine 执行差异的探究

本文深入探讨了 Go 语言调度器在处理并发任务时,循环次数的奇偶性如何影响 Goroutine 的执行结果。通过分析一个简单的示例,揭示了程序退出时未完成的 Goroutine 可能被中断的现象,并提出了使用 `sync.WaitGroup` 等机制确保 Goroutine 完成的方法。此外,还对示例代码进行了简化和优化,使其更符合 Go 语言的编程习惯。

在 Go 语言中,Goroutine 是一种轻量级的并发执行单元。Go 调度器负责在不同的 Goroutine 之间分配 CPU 时间片,从而实现并发执行。然而,在某些情况下,程序的行为可能会受到循环次数等因素的影响,导致 Goroutine 的执行结果出现差异。

以下面这段 Go 代码为例:

package main

import "runtime"

func main() {
    c2 := make(chan int)

    go func() {
        for v := range c2 {
            println("c2 =", v, "numof routines:", runtime.NumGoroutine())
        }
    }()

    for i := 1; i <= 10001; i++ { // 或者 i <= 10000
        c2 <- i
        //runtime.Gosched()
    }
}
登录后复制

这段代码创建了一个 Goroutine,用于从 channel c2 中接收数据并打印。主 Goroutine 向 c2 发送一系列整数。一个令人困惑的现象是:当循环次数为奇数(例如 10001)时,所有数字都能被 Goroutine 接收并打印;而当循环次数为偶数(例如 10000)时,最后一个数字可能无法被打印。

原因分析

这种现象的原因在于 Go 程序的退出机制。当 main 函数返回时,程序会立即终止,而不会等待所有 Goroutine 完成。因此,Goroutine 是否能够完成其任务,取决于调度器的调度策略以及一些随机因素。循环次数的微小差异(例如 10000 和 10001)可能会影响调度器的决策,从而导致 Goroutine 在 main 函数退出前是否能够完成所有任务。

更具体地说,当循环次数较小时,main 函数完成得更快,Goroutine 可能没有足够的时间来处理 channel 中的所有数据。而当循环次数较大时,main 函数花费的时间更长,Goroutine 有更大的机会完成其任务。

解决方案

Replit Ghostwrite
Replit Ghostwrite

一种基于 ML 的工具,可提供代码完成、生成、转换和编辑器内搜索功能。

Replit Ghostwrite 93
查看详情 Replit Ghostwrite

为了确保 Goroutine 在程序退出前完成所有任务,可以使用 sync.WaitGroup 等机制来等待 Goroutine 完成。以下是使用 sync.WaitGroup 的示例代码:

package main

import (
    "fmt"
    "runtime"
    "sync"
)

func main() {
    c2 := make(chan int)
    var wg sync.WaitGroup

    wg.Add(1) // 增加一个等待的 Goroutine

    go func() {
        defer wg.Done() // Goroutine 完成后,减少等待计数
        for v := range c2 {
            fmt.Println("c2 =", v, "numof routines:", runtime.NumGoroutine())
        }
    }()

    for i := 1; i <= 10000; i++ {
        c2 <- i
        //runtime.Gosched()
    }
    close(c2) // 关闭 channel,通知 Goroutine 停止接收数据

    wg.Wait() // 等待所有 Goroutine 完成
}
登录后复制

在这个修改后的版本中,sync.WaitGroup 用于等待 Goroutine 完成。wg.Add(1) 增加了一个等待的 Goroutine。在 Goroutine 内部,defer wg.Done() 确保在 Goroutine 退出时,等待计数器会减 1。close(c2) 关闭了 channel,通知 Goroutine 停止接收数据。wg.Wait() 阻塞 main 函数,直到所有 Goroutine 完成。

代码优化

原始代码中 Goroutine 的循环可以简化为:

for v := range c2 {
    println("c2 =", v,"numof routines:",runtime.NumGoroutine())
}
登录后复制

通过 range 遍历 channel,可以更简洁地处理 channel 中的数据。

总结与注意事项

  • Go 程序的退出机制不会等待所有 Goroutine 完成,因此需要使用 sync.WaitGroup 等机制来确保 Goroutine 完成。
  • 循环次数等因素可能会影响调度器的调度策略,从而导致 Goroutine 的执行结果出现差异。
  • 应该始终使用 close 关闭 channel,以通知 Goroutine 停止接收数据。
  • 代码应该尽可能简洁和清晰,遵循 Go 语言的编程习惯。

理解 Go 调度器的工作原理对于编写高效和可靠的并发程序至关重要。通过本文的分析,希望能帮助读者更好地理解 Go 语言的并发机制,并避免在实际开发中遇到类似的问题。

以上就是Go 调度器:奇偶循环次数导致 Goroutine 执行差异的探究的详细内容,更多请关注php中文网其它相关文章!

相关标签:
最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号