例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
的地址始終是不變的,所以最終取出來的值都變成一樣的了