通过迭代函数切片调用每个函数
calling each function by iteration function slice
我正在尝试循环一段函数,然后调用其中的每个函数。但是我得到了奇怪的结果。这是我的代码:
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
请指出问题所在及其背后的逻辑。另外我怎样才能得到想要的行为。
经典的陷阱:)
官方围棋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(意味着内存中的相同地址,因此具有相同的值),因此它们自然都会看到 [=13= 的最后一个值].
解决方案是在关闭之前重新声明 a:=a
(如上)。这将创建新的 var(内存中的新地址),然后对于 go func
.
的每次调用都是新的
或者将其作为参数传递给 go 函数,在这种情况下,您传递 a
值的副本,如下所示:
go func(i int) {
defer wg.Done()
f[i]()
}(a)
您甚至不需要 go 例程,这个 https://play.golang.org/p/nkP9YfeOWF 例如演示了相同的陷阱。这里的关键是'closure'.
问题似乎是您没有将所需的值传递给 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,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()
}
我正在尝试循环一段函数,然后调用其中的每个函数。但是我得到了奇怪的结果。这是我的代码:
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
请指出问题所在及其背后的逻辑。另外我怎样才能得到想要的行为。
经典的陷阱:)
官方围棋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(意味着内存中的相同地址,因此具有相同的值),因此它们自然都会看到 [=13= 的最后一个值].
解决方案是在关闭之前重新声明 a:=a
(如上)。这将创建新的 var(内存中的新地址),然后对于 go func
.
或者将其作为参数传递给 go 函数,在这种情况下,您传递 a
值的副本,如下所示:
go func(i int) {
defer wg.Done()
f[i]()
}(a)
您甚至不需要 go 例程,这个 https://play.golang.org/p/nkP9YfeOWF 例如演示了相同的陷阱。这里的关键是'closure'.
问题似乎是您没有将所需的值传递给 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,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()
}