Joh*_*now 2 closures pointers go
这是代码:
type field struct {
name string
}
func print(p *field) {
fmt.Println(p.name)
}
func fix1() {
data := []*field{{name: "one"}, {name: "two"}, {name: "three"}}
for _, v := range data {
go print(v)
}
time.Sleep(time.Millisecond * 200)
}
func wrong1() {
data := []*field{{name: "one"}, {name: "two"}, {name: "three"}}
for _, v := range data {
go func() {
print(v)
}()
}
time.Sleep(time.Millisecond * 200)
}
func main() {
wrong1()
}
Run Code Online (Sandbox Code Playgroud)
据我所知,函数中的所有goroutine wrong1共享相同的局部变量v.在执行goroutine时,值v可能等于任何值data,因此该函数会打印三次随机数据.
但是,我无法理解为什么函数的fix1行为不同(它只打印data一次每个值).
go func() { print(v) }()使用具有并发性的闭包时可能会出现一些混淆.考虑以下程序:
Run Code Online (Sandbox Code Playgroud)func main() { done := make(chan bool) values := []string{"a", "b", "c"} for _, v := range values { go func() { fmt.Println(v) done <- true }() } // wait for all goroutines to complete before exiting for _ = range values { <-done } }人们可能错误地期望看到a,b,c作为输出.您可能会看到的是c,c,c.这是因为循环的每次迭代都使用变量v的相同实例,因此每个闭包共享该单个变量.当封闭运行,它打印在执行fmt.Println时间v的值,但V可以被修改,因为够程推出.
要在启动时将v的当前值绑定到每个闭包,必须修改内部循环以在每次迭代时创建新变量.一种方法是将变量作为参数传递给闭包:
Run Code Online (Sandbox Code Playgroud)for _, v := range values { go func(u string) { fmt.Println(u) done <- true }(v) }在此示例中,v的值作为参数传递给匿名函数.然后可以在函数内部访问该值作为变量u.
更简单的是创建一个新的变量,使用一个看似奇怪的声明样式但在Go中运行正常:
Run Code Online (Sandbox Code Playgroud)for _, v := range values { v := v // create a new 'v'. go func() { fmt.Println(v) done <- true }() }
你的wrong1例子,
for _, v := range data {
go func() {
print(v)
}()
}
Run Code Online (Sandbox Code Playgroud)
游乐场:https://play.golang.org/p/0w86nvVMt1g
输出:
three
three
three
Run Code Online (Sandbox Code Playgroud)
你的wrong1例子,创建一个新变量,
for _, v := range data {
v := v
go func() {
print(v)
}()
}
Run Code Online (Sandbox Code Playgroud)
游乐场:https://play.golang.org/p/z5RCI0ZZU8Z
输出:
one
two
three
Run Code Online (Sandbox Code Playgroud)
你的wrong1例子,将变量作为参数传递,
for _, v := range data {
go func(v *field) {
print(v)
}(v)
}
Run Code Online (Sandbox Code Playgroud)
游乐场:https://play.golang.org/p/1JVI7XYSqvv
输出:
one
two
three
Run Code Online (Sandbox Code Playgroud)
go print(v)给定函数类型F的表达式f,
Run Code Online (Sandbox Code Playgroud)f(a1, a2, … an)用参数a1,a2,... a调用f.除了一种特殊情况,参数必须是可赋值给F的参数类型的单值表达式,并在调用函数之前进行求值.
函数值和参数在调用goroutine中照常评估.
你的fix1例子,评估v调用函数之前的值,
for _, v := range data {
go print(v)
}
Run Code Online (Sandbox Code Playgroud)
游乐场:https://play.golang.org/p/rN3UNaGi-ge
输出:
one
two
three
Run Code Online (Sandbox Code Playgroud)