Golang中循环内使用Goroutines的实践与技巧
Golang中循环内使用Goroutines的实践与技巧 在这段代码中:https://play.golang.org/p/wPt5RwYy3ij
package main
import (
"time"
)
func main() {
for i := 0; i < 2; i++ {
go func() {
println(i)
}()
}
time.Sleep(1)
}
输出:
2
2
为什么?
如果将 i 作为参数传递给协程函数,那么它可以正常工作。但我无法理解为什么 i 会取切片的最大长度值。
3 回复
看看这个:https://play.golang.org/p/eCtEpBUM4wc
基本上,goroutine 在循环退出之前不会执行,这就是为什么通常使用数据通道来推送值,然后由 goroutine 来消费。
查看这个通道实现的示例:https://play.golang.org/p/duV3JgU4yWJ
更多关于Golang中循环内使用Goroutines的实践与技巧的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go语言中,这个问题是由于闭包捕获变量的特性导致的。当在循环内创建goroutine并使用外部变量i时,所有goroutine都共享同一个i变量的引用,而不是在创建时捕获i的当前值。
问题分析
在你的代码中:
for i := 0; i < 2; i++ {
go func() {
println(i) // 这里捕获的是i的引用,不是值
}()
}
执行过程:
- 循环启动两个goroutine
- goroutine被调度执行时,循环可能已经结束
- 此时
i的值已经变为2(循环终止条件) - 两个goroutine都打印最终的
i值:2
解决方案
方法1:传递参数(推荐)
package main
import (
"time"
)
func main() {
for i := 0; i < 2; i++ {
go func(x int) {
println(x)
}(i) // 传递i的当前值
}
time.Sleep(time.Millisecond)
}
方法2:在循环内创建局部变量
package main
import (
"time"
)
func main() {
for i := 0; i < 2; i++ {
i := i // 创建局部变量
go func() {
println(i)
}()
}
time.Sleep(time.Millisecond)
}
方法3:使用WaitGroup等待所有goroutine完成
package main
import (
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 2; i++ {
wg.Add(1)
go func(x int) {
defer wg.Done()
println(x)
}(i)
}
wg.Wait() // 等待所有goroutine完成
}
输出结果
使用上述任一方法,输出都会是:
0
1
关键点
- goroutine的调度时机不确定
- 闭包捕获的是变量的引用,不是创建时的值
- 传递参数或创建局部变量可以解决这个问题
- 在生产代码中应该使用
sync.WaitGroup而不是time.Sleep

