0

0

Go 调度器奇偶行为探究:并发程序中的非确定性与同步机制

DDD

DDD

发布时间:2025-10-16 11:05:13

|

316人浏览过

|

来源于php中文网

原创

go 调度器奇偶行为探究:并发程序中的非确定性与同步机制

本文深入探讨了 Go 语言并发程序中一个有趣的现象:当循环次数为奇数时,程序能够完整输出所有数值;而当循环次数为偶数时,最后一个数值却丢失。通过分析代码示例和调度器行为,揭示了并发程序中非确定性的本质,并强调了使用同步机制确保 Goroutine 完成的重要性。文章将帮助读者理解 Go 调度器的工作方式,并掌握编写可靠并发程序的关键技巧。

在 Go 语言中,并发编程是一项强大的特性,它允许程序同时执行多个任务。然而,并发也引入了非确定性,这意味着程序的行为可能因运行环境、调度器的决策等因素而异。本文将通过一个具体的例子,深入探讨 Go 调度器在并发程序中的行为,并解释为何循环次数的奇偶性会影响程序的输出结果。

问题描述

考虑以下 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++ {
        c2 <- i
        // runtime.Gosched()
    }
}

这段代码创建了一个 Goroutine,它从通道 c2 中接收整数并打印。主 Goroutine 向 c2 发送从 1 到 10001(或 10000)的整数。

令人惊讶的是,当循环次数为奇数(例如 10001)时,程序能够完整输出所有数值。但是,当循环次数为偶数(例如 10000)时,程序会丢失最后一个数值。

原因分析:调度器的非确定性

这种现象的根本原因是 Go 调度器的非确定性。当 main 函数返回时,程序会终止,而不会等待任何 Goroutine 完成。因此,Goroutine 是否能在 main 函数返回之前完成所有工作,取决于调度器的调度策略以及一些外部因素。

循环次数的奇偶性可能只是影响调度的一个因素。当循环次数为偶数时,可能由于某种巧合,调度器在 Goroutine 处理完所有数据之前就切换回了 main Goroutine,导致 main 函数提前返回,从而导致最后一个数值丢失。

Sora
Sora

Sora是OpenAI发布的一种文生视频AI大模型,可以根据文本指令创建现实和富有想象力的场景。

下载

解决方案:使用同步机制

为了确保 Goroutine 在 main 函数返回之前完成所有工作,我们需要使用同步机制。Go 提供了多种同步机制,例如 sync.WaitGroup。

以下是如何使用 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) // 关闭通道,通知 Goroutine 没有更多数据了

    wg.Wait() // 等待计数器归零,表示所有 Goroutine 都已完成
}

在这个修改后的代码中,我们使用 sync.WaitGroup 来等待 Goroutine 完成。

  1. wg.Add(1) 增加计数器,表示有一个 Goroutine 需要等待。
  2. defer wg.Done() 在 Goroutine 完成时减少计数器。
  3. close(c2) 关闭通道,通知 Goroutine 没有更多数据了。这是很关键的一步,否则goroutine会一直阻塞等待通道的数据。
  4. wg.Wait() 等待计数器归零,表示所有 Goroutine 都已完成。

通过使用 sync.WaitGroup,我们可以确保 main 函数在所有 Goroutine 完成工作后才返回,从而避免了数据丢失的问题。

总结

Go 调度器的非确定性是并发编程中需要注意的一个重要问题。为了编写可靠的并发程序,我们需要使用适当的同步机制来确保 Goroutine 在程序退出之前完成所有工作。sync.WaitGroup 是一个常用的同步机制,可以用于等待一组 Goroutine 完成。此外,使用 close 关闭channel也是通知goroutine不再有数据输入的重要手段。理解这些概念并正确应用它们,可以帮助我们编写出更加健壮和可靠的 Go 并发程序。

相关专题

更多
Golang channel原理
Golang channel原理

本专题整合了Golang channel通信相关介绍,阅读专题下面的文章了解更多详细内容。

246

2025.11.14

golang channel相关教程
golang channel相关教程

本专题整合了golang处理channel相关教程,阅读专题下面的文章了解更多详细内容。

342

2025.11.17

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

19

2026.01.20

PS使用蒙版相关教程
PS使用蒙版相关教程

本专题整合了ps使用蒙版相关教程,阅读专题下面的文章了解更多详细内容。

61

2026.01.19

java用途介绍
java用途介绍

本专题整合了java用途功能相关介绍,阅读专题下面的文章了解更多详细内容。

87

2026.01.19

java输出数组相关教程
java输出数组相关教程

本专题整合了java输出数组相关教程,阅读专题下面的文章了解更多详细内容。

39

2026.01.19

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

10

2026.01.19

xml格式相关教程
xml格式相关教程

本专题整合了xml格式相关教程汇总,阅读专题下面的文章了解更多详细内容。

13

2026.01.19

PHP WebSocket 实时通信开发
PHP WebSocket 实时通信开发

本专题系统讲解 PHP 在实时通信与长连接场景中的应用实践,涵盖 WebSocket 协议原理、服务端连接管理、消息推送机制、心跳检测、断线重连以及与前端的实时交互实现。通过聊天系统、实时通知等案例,帮助开发者掌握 使用 PHP 构建实时通信与推送服务的完整开发流程,适用于即时消息与高互动性应用场景。

19

2026.01.19

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 4万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

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