我试图循环一片函数,然后调用其中的每个函数。但是我得到了奇怪的结果。这是我的代码:
package main
import (
"fmt"
"sync"
)
func A() {
fmt.Println("A")
}
func B() {
fmt.Println("B")
}
func C() {
fmt.Println("C")
}
func main() {
type fs func()
var wg sync.WaitGroup
f := []fs{A, B, C}
for a, _ := range f {
wg.Add(1)
go func() {
defer wg.Done()
f[a]()
}()
}
wg.Wait()
}
我原以为它会调用函数A,B然后调用C,但我的输出只得到Cs。
C
C
C
请提出错误及其背后的逻辑。我怎样才能得到理想的行为。
答案 0 :(得分:5)
Classic go gotcha :)
Go Go FAQ
for a, _ := range f {
wg.Add(1)
a:=a // this will make it work
go func() {
defer wg.Done()
f[a]()
}()
}
您的func() {}()
是一个关闭a
的关闭。并且a
是所有go func
go例程的共享,因为for循环重用相同的var(在内存中意味着相同的地址,因此值相同),所以它们自然都会看到{{1}的最后一个值}。
解决方案要么在关闭之前重新声明a
(如上所述)。这将创建新的var(内存中的新地址),然后每次调用a:=a
时都是新的。
或者将其作为参数传递给go函数,在这种情况下,您传递go func
值的副本,如下所示:
a
你甚至不需要去例行https://play.golang.org/p/nkP9YfeOWF,例如演示同样的问题。这里的关键是'封闭'。
答案 1 :(得分:2)
问题似乎是你没有将所需的值传递给goroutine,而变量值是从外部范围获取的。话虽这么说,范围迭代甚至在第一个goroutine执行之前就完成了,这就是为什么你总是得到索引a == 2,即函数C. 如果你只是将time.Sleep(100)放在你的范围内,只是为了让goroutine能够在继续下一次迭代之前赶上主线程,你可以测试这个 - > GO playground
for a, _ := range f {
wg.Add(1)
go func() {
defer wg.Done()
f[a]()
}()
time.Sleep(100)
}
输出
A
B
C
虽然你想要做的只是简单地将一个参数传递给goroutine,它将为该函数制作一个副本。
func main() {
type fs func()
var wg sync.WaitGroup
f := []fs{A, B, C}
for _, v := range f {
wg.Add(1)
go func(f fs) {
defer wg.Done()
f()
}(v)
}
wg.Wait()
}