请检查以下代码段:
package main import ( "fmt" "time" ) type field struct { name string } func (p *field) print() { fmt.Println(p.name) } func main() { fmt.Println("use values:") // use values in range loop and go rountines values := []field{{"one"},{"two"},{"three"}} for _, v := range values { go v.print() } time.Sleep(time.Second) fmt.Println() fmt.Println("use pointers:") // use pointers in range loop and go rountines poniters := []*field{{"one"},{"two"},{"three"}} for _, v := range poniters { go v.print() } time.Sleep(time.Second) }
链接到这里:https : //play.golang.org/p/cdryPmyWt5
上面的代码将检查for循环中的指针和值之间的差异,而go语句也同时使用。对于代码:
values := []field{{"one"},{"two"},{"three"}} for _, v := range values { go v.print() }
我们知道,控制台将打印 三三三 的结果,因为循环运行到其最终的够程开始执行,其中写V作为片的最后一个元素之前。但是指针呢?
poniters := []*field{{"one"},{"two"},{"three"}} for _, v := range poniters { go v.print() }
好像打印 一二三 ,为什么呢?
谢谢。
答:在调用函数之前先评估参数。在对它们进行评估之后,调用的参数将按值传递给函数,并且被调用函数开始执行,因此:
第一go v.print()是语法糖go (*field).print(&v)和 所述第二go v.print()是语法糖go (*field).print(v)。
go v.print()
go (*field).print(&v)
go (*field).print(v)
如果for在goroutines启动之前第一个循环完成,&v则调用相同,这三个调用都相同。通过time.Sleep(100)在go v.print()第一个循环之后添加,请参见代码2 。或go func(v field) { v.print() }(v)在The Go Playground(带有的代码3 sync.WaitGroup)上使用。 另外,您在这里有 数据竞赛 (请参阅B)。
for
&v
time.Sleep(100)
go func(v field) { v.print() }(v)
sync.WaitGroup
而对于第二个go (*field).print(v)位置v是之前调用参数评估真实指针和三个够程print,并 有三种不同的地址 。
v
print
1-在The Go Playground上尝试:
package main import ( "fmt" "time" ) type field struct { name string } func (p *field) print() { fmt.Println(p.name) } func main() { fmt.Println("use values:") // use values in range loop and go rountines values := []field{{"one"}, {"two"}, {"three"}} for _, v := range values { fmt.Println(&v) go (*field).print(&v) //go v.print() } time.Sleep(time.Second) fmt.Println() fmt.Println("use pointers:") // use pointers in range loop and go rountines poniters := []*field{{"one"}, {"two"}, {"three"}} for _, v := range poniters { fmt.Println(v) go (*field).print(v) //go v.print() } time.Sleep(time.Second) }
输出:
use values: &{one} &{two} &{three} three three three use pointers: &{one} &{two} &{three} two one three
2-在Go Playground上尝试:
package main import ( "fmt" "time" ) type field struct { name string } func (p *field) print() { fmt.Println(p.name) } func main() { fmt.Println("use values:") // use values in range loop and go rountines values := []field{{"one"}, {"two"}, {"three"}} for _, v := range values { fmt.Println(&v) go v.print() //go (*field).print(&v) // time.Sleep(100) } time.Sleep(time.Second) fmt.Println() fmt.Println("use pointers:") // use pointers in range loop and go rountines poniters := []*field{{"one"}, {"two"}, {"three"}} for _, v := range poniters { fmt.Println(v) go v.print() //go (*field).print(v) // } time.Sleep(time.Second) }
use values: &{one} one &{two} two &{three} three use pointers: &{one} &{two} &{three} one two three
B:您之间存在数据争夺,请尝试go build -race 您的代码,然后运行生成的文件WARNING: DATA RACE:
go build -race
WARNING: DATA RACE
use values: ================== WARNING: DATA RACE Read at 0x00c042030210 by goroutine 6: runtime.convT2E() Go/src/runtime/iface.go:155 +0x0 main.(*field).print() .../m.go:14 +0x6c Previous write at 0x00c042030210 by main goroutine: main.main() .../m.go:22 +0x1c3 Goroutine 6 (running) created at: main.main() .../m.go:23 +0x204 ================== two three three use pointers: one two three Found 1 data race(s)