
本文深入探讨了 Go 语言并发程序中一个有趣的现象:当循环次数为奇数时,程序能够完整输出所有数值;而当循环次数为偶数时,最后一个数值却丢失。通过分析代码示例和调度器行为,揭示了并发程序中非确定性的本质,并强调了使用同步机制确保 Goroutine 完成的重要性。文章将帮助读者理解 Go 调度器的工作方式,并掌握编写可靠并发程序的关键技巧。
在 Go 语言中,并发编程是一项强大的特性,它允许程序同时执行多个任务。然而,并发也引入了非确定性,这意味着程序的行为可能因运行环境、调度器的决策等因素而异。本文将通过一个具体的例子,深入探讨 Go 调度器在并发程序中的行为,并解释为何循环次数的奇偶性会影响程序的输出结果。
问题描述
考虑以下 Go 代码:
package mainimport "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 函数提前返回,从而导致最后一个数值丢失。
Otter.ai
一个自动的会议记录和笔记工具,会议内容生成和实时转录
91 查看详情
解决方案:使用同步机制
为了确保 Goroutine 在 main 函数返回之前完成所有工作,我们需要使用同步机制。Go 提供了多种同步机制,例如 sync.WaitGroup。
以下是如何使用 sync.WaitGroup 修改代码以确保所有数值都被处理:
package mainimport ( "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 完成。
wg.Add(1) 增加计数器,表示有一个 Goroutine 需要等待。defer wg.Done() 在 Goroutine 完成时减少计数器。close(c2) 关闭通道,通知 Goroutine 没有更多数据了。这是很关键的一步,否则goroutine会一直阻塞等待通道的数据。wg.Wait() 等待计数器归零,表示所有 Goroutine 都已完成。
通过使用 sync.WaitGroup,我们可以确保 main 函数在所有 Goroutine 完成工作后才返回,从而避免了数据丢失的问题。
总结
Go 调度器的非确定性是并发编程中需要注意的一个重要问题。为了编写可靠的并发程序,我们需要使用适当的同步机制来确保 Goroutine 在程序退出之前完成所有工作。sync.WaitGroup 是一个常用的同步机制,可以用于等待一组 Goroutine 完成。此外,使用 close 关闭channel也是通知goroutine不再有数据输入的重要手段。理解这些概念并正确应用它们,可以帮助我们编写出更加健壮和可靠的 Go 并发程序。
以上就是Go 调度器奇偶行为探究:并发程序中的非确定性与同步机制的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1113730.html
微信扫一扫
支付宝扫一扫