Go 通过迭代函数片调用每个函数

Go 通过迭代函数片调用每个函数,go,slice,goroutine,Go,Slice,Goroutine,我试图循环函数的一个片段,然后调用其中的每个函数。然而,我得到了奇怪的结果。这是我的密码: 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.Wai

我试图循环函数的一个片段,然后调用其中的每个函数。然而,我得到了奇怪的结果。这是我的密码:

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
请说明哪里出了问题以及背后的逻辑。还有,我怎样才能得到想要的行为

经典版go gotcha:)

官方围棋

您的
func(){}()
是一个关闭
a
的闭包。而
a
是所有
go func
go例程的共享,因为for循环重用相同的var(意味着内存中的相同地址,因此值相同),所以它们自然都会看到
a
的最后一个值

解决方案是在闭包之前重新声明
a:=a
(如上所述)。这将创建新的var(内存中的新地址),该var对于每次调用
go func
都是新的

或者将其作为参数传递给go函数,在这种情况下,可以传递
a
值的副本,如下所示:

go func(i int) {
    defer wg.Done()
    f[i]()
}(a)

你甚至不需要有围棋程序,这个例子演示了同样的问题。这里的关键是“闭包”。

问题似乎是您没有将所需的值传递给goroutine,并且变量值是从外部范围获取的。这就是说,范围迭代甚至在执行第一个goroutine之前就完成了,这就是为什么总是得到索引a==2,这就是函数C。 如果您只需将time.Sleep(100)放在您的范围内,就可以测试这一点,只是为了让goroutine在继续下一次迭代之前赶上主线程-->

输出

A
B
C
尽管您要做的只是简单地向goroutine传递一个参数,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()
}

A
B
C
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()
}