我遇到了Go for循环和WaitGroup的问题



我是一个正在学习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)
}
}
  1. 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}
  1. 简单且干净写:
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})

  1. 既然你使用的是闭包,你不需要传递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}]

最新更新