我已经找到了一种方法让代码按照我的意愿运行,但我想了解为什么它的行为如此,以便我对Go并发性的理解得到改善。
我正在测试sync.WaitGroup
等待一些goroutines完成,因为我计划以这种方式多次上传到Amazon S3。
这是我原来的代码:
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go func() {
fmt.Println(i)
time.Sleep(time.Second * 1)
wg.Done()
}()
}
wg.Wait()
}
我很惊讶地看到输出是:6, 6, 6, 6, 6
。
而不是像2, 4, 1, 5, 3
。
由于循环甚至没有达到6,这对我来说没有任何意义。
我后来将i
变量作为参数传递给匿名函数
然后它表现得像我预期的那样。
为什么会这样?我不明白。
答案 0 :(得分:9)
常见问题解答:What happens with closures running as goroutines?
在这种情况下,在for循环完成之前,没有任何goroutine被调度。为了使for循环中断i
不能小于或等于5,因此在那时它是6。当goroutines运行时,它们每个都打印在闭包中捕获的单个变量i
的值。
将i
作为参数传递给函数时,将当前值复制到新变量,捕获此时的值。
答案 1 :(得分:0)
要回答您的问题,您必须将i
传递到func
,以便每个例程都有自己的i
值副本。
所以你的代码应该是这样的
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go func(i int) {
fmt.Println(i)
time.Sleep(time.Second * 1)
wg.Done()
}(i)
}
wg.Wait()
}
我编写了这个实用程序函数来帮助并行化一组函数:
import "sync"
// Parallelize parallelizes the function calls
func Parallelize(functions ...func()) {
var waitGroup sync.WaitGroup
waitGroup.Add(len(functions))
defer waitGroup.Wait()
for _, function := range functions {
go func(copy func()) {
defer waitGroup.Done()
copy()
}(function)
}
}
所以在你的情况下,我们可以做到这一点
func main() {
functions := []func(){}
for i := 1; i <= 5; i++ {
function := func(i int) func() {
return func() {
fmt.Println(i)
}
}(i)
functions = append(functions, function)
}
Parallelize(functions...)
fmt.Println("Done")
}
如果您想使用Parallelize功能,可以在https://github.com/shomali11/util
找到它