我是一个正在学习Go的新手,我遇到了如下问题:
type Foo struct {
Name string
Result int
}
func main() {
foos := make([]Foo, 0)
foos = append(foos, Foo{"a", 0})
foos = append(foos, Foo{"bb", 0})
foos = append(foos, Foo{"ccc", 0})
var wg sync.WaitGroup
for _, f := range foos {
wg.Add(1)
go func(f *Foo, wg *sync.WaitGroup) {
f.Result = len(f.Name)
wg.Done()
}(&f, &wg)
}
wg.Wait()
fmt.Println(foos)
}
我期望输出是[{a 1} {bb 2} {ccc 3}]
,但实际输出是[{a 0} {bb 0} {ccc 0}]
。
我不知道为什么。如果有人能解答我的困惑,我将不胜感激。
当输入foo时,f
的值是foos
元素的副本。因此,更改副本的Result
与foos中的元素无关。
要得到您期望的结果,您可以尝试下面的代码,使用指针代替。
package main
import (
"fmt"
"sync"
)
type Foo struct {
Name string
Result int
}
func main() {
foos := make([]*Foo, 0)
foos = append(foos, &Foo{"a", 0})
foos = append(foos, &Foo{"bb", 0})
foos = append(foos, &Foo{"ccc", 0})
var wg sync.WaitGroup
for _, f := range foos {
wg.Add(1)
go func(f *Foo, wg *sync.WaitGroup) {
f.Result = len(f.Name)
wg.Done()
}(f, &wg)
}
wg.Wait()
for _, f := range foos {
fmt.Println(*f)
}
}
f
是本地变量在for
循环内,所以它只是一个本地拷贝元素的值,它在每次循环迭代时都会发生变化,尝试这样查看地址(是常量),以及每次循环迭代时f
的值:
package main
import "fmt"
func main() {
foos := []Foo{{"a", 0}, {"bb", 0}, {"ccc", 0}}
for _, f := range foos {
fmt.Printf("%p %vn", &f, f)
}
}
type Foo struct {
Name string
Result int
}
输出:
0xc00010c000 {a 0}
0xc00010c000 {bb 0}
0xc00010c000 {ccc 0}
- 简单且干净写:
foos := []Foo{{"a", 0}, {"bb", 0}, {"ccc", 0}}
代替:
foos := make([]Foo, 0)
foos = append(foos, Foo{"a", 0})
foos = append(foos, Foo{"bb", 0})
foos = append(foos, Foo{"ccc", 0})
- 既然你使用的是闭包,你不需要传递
WaitGroup
引用,切片的索引更干净和足够的:
wg := &sync.WaitGroup{}
for i := range foos {
wg.Add(1)
go func(i int) {
foos[i].Result = len(foos[i].Name)
wg.Done()
}(i)
}
Try it all:
package main
import (
"fmt"
"sync"
)
func main() {
foos := []Foo{{"a", 0}, {"bb", 0}, {"ccc", 0}}
wg := &sync.WaitGroup{}
for i := range foos {
wg.Add(1)
go func(i int) {
foos[i].Result = len(foos[i].Name)
wg.Done()
}(i)
}
wg.Wait()
fmt.Println(foos)
}
type Foo struct {
Name string
Result int
}
输出:
[{a 1} {bb 2} {ccc 3}]