例1:
package main
import (
"fmt"
"time"
)
func main() {
data := []string{"one","two","three"}
for _,v := range data {
go func() {
fmt.Println(v)
}()
}
time.Sleep(3 * time.Second)
//goroutines print: three, three, three
}
package main
import (
"fmt"
"time"
)
func main() {
data := []string{"one","two","three"}
for _,v := range data {
vcopy := v //
go func() {
fmt.Println(vcopy)
}()
}
time.Sleep(3 * time.Second)
//goroutines print: one, two, three
}
这里应该很好理解,闭包绑定了v,v的值会因为遍历不停的覆盖,最终变为最后一个元素。可以参考。所以需要在for中创建一个临时变量来解决。
例2:
package main
import (
"fmt"
"runtime"
"time"
)
type field struct {
name string
}
func (p *field) print() {
fmt.Println(p.name)
}
func (p field) print1() {
fmt.Println(p.name)
}
func myprint(p *field) {
fmt.Println(p.name)
}
func myprint2(p field) {
fmt.Println(p.name)
}
func main() {
runtime.GOMAXPROCS(1)
data1 := []*field{{"one"}, {"two"}, {"three"}}
for _, v := range data1 {
go v.print()
//go myprint(v)
//go v.print1()
}
time.Sleep(time.Second)
data2 := []field{{"four"}, {"five"}, {"six"}}
for _, v := range data2 {
go v.print()
//go myprint(&v)
}
time.Sleep(time.Second)
// print: one two three six six six
}
为什么值slice
就输出一样的,但指针slice
就没有问题呢?
首先看下群友“橘子汽水”的解答
循环里的v一直在被修改,但v的地址没变。第一个循环,v其实是指针类型,这样v的值可以传进
struct
,但是&v.print
。但是v这个变量在循环内地址是固定的,因此3个
值slice
遍历的时候,因为print
是指针接收者方法,所以编译器会把v
转&v
,因为v
的地址是不会变的,所以其他的goroutine
通过地址读取出来都是相同的值。
其实golang
中接收者方法是一个语法糖,我们可以通过这个来帮助理解。
func (p field) print1() {
fmt.Println(p.name)
}
p.print1() // 等同于print1(p)
所以例2中,可以用myprint
来便于理解,值slice遍历时,因为第一个参数是指针类型,所以需要取指针myprint(&v)
,但是v
的地址始终是不变的,所以最终取出来的值都变成一样的了