考虑以下Golang代码(也在Go Playground上):
package main
import "fmt"
import "time"
func main() {
for _, s := range []string{"foo", "bar"} {
x := s
func() {
fmt.Printf("s: %s\n", s)
fmt.Printf("x: %s\n", x)
}()
}
fmt.Println()
for _, s := range []string{"foo", "bar"} {
x := s
go func() {
fmt.Printf("s: %s\n", s)
fmt.Printf("x: %s\n", x)
}()
}
time.Sleep(time.Second)
}
Run Code Online (Sandbox Code Playgroud)
此代码生成以下输出:
s: foo
x: foo
s: bar
x: bar
s: bar
x: foo
s: bar
x: bar
Run Code Online (Sandbox Code Playgroud)
假设这不是一些奇怪的编译器错误,我很好奇为什么a)s的值在goroutine版本中然后在常规func调用中被解释不同而b)以及为什么将它分配给循环内部的局部变量两种情况.
Mit*_*ell 10
Go中的闭包是词法范围.这意味着从"外部"范围在闭包内引用的任何变量都不是副本,而实际上是引用.一for环竟重复使用相同的变量多次,所以你介绍的读/写的之间的竞争条件s变量.
但是x分配一个新变量(带有:=)和复制s,这导致每次都是正确的结果.
通常,最佳做法是传入您想要的任何参数,以便您没有引用.例:
for _, s := range []string{"foo", "bar"} {
x := s
go func(s string) {
fmt.Printf("s: %s\n", s)
fmt.Printf("x: %s\n", x)
}(s)
}
Run Code Online (Sandbox Code Playgroud)
提示: 可以使用“获取地址运算符”&来确认变量是否相同。
让我们稍微修改一下您的程序以帮助我们理解。
package main
import "fmt"
import "time"
func main() {
for _, s := range []string{"foo", "bar"} {
x := s
fmt.Println(" &s =", &s, "\t&x =", &x)
func() {
fmt.Println("-", "&s =", &s, "\t&x =", &x)
fmt.Println("s =", s, ", x =", x)
}()
}
fmt.Println("\n\n")
for _, s := range []string{"foo", "bar"} {
x := s
fmt.Println(" &s =", &s, "\t&x =", &x)
go func() {
fmt.Println("-", "&s =", &s, "\t&x =", &x)
fmt.Println("s =", s, ", x =", x)
}()
}
time.Sleep(time.Second)
}
Run Code Online (Sandbox Code Playgroud)
输出是:
&s = 0x1040a120 &x = 0x1040a128
- &s = 0x1040a120 &x = 0x1040a128
s = foo , x = foo
&s = 0x1040a120 &x = 0x1040a180
- &s = 0x1040a120 &x = 0x1040a180
s = bar , x = bar
&s = 0x1040a1d8 &x = 0x1040a1e0
&s = 0x1040a1d8 &x = 0x1040a1f8
- &s = 0x1040a1d8 &x = 0x1040a1e0
s = bar , x = foo
- &s = 0x1040a1d8 &x = 0x1040a1f8
s = bar , x = bar
Run Code Online (Sandbox Code Playgroud)
关键点:
s循环的每次迭代中的变量都是相同的变量。x循环每次迭代中的局部变量都是不同的变量,它们只是碰巧具有相同的名称xfunc () {} ()部分在每次迭代中执行,并且循环仅在func () {} ()完成后继续到下一次迭代。go func () {} ()语句本身立即完成。func 主体中的语句何时执行由 Go 调度程序决定。但是当它们(func体中的语句)开始执行时,for循环已经完成了!变量s是切片中的最后一个元素,即bar。这就是为什么我们在第二个 for 循环输出中得到两个“bar”。| 归档时间: |
|
| 查看次数: |
2422 次 |
| 最近记录: |