fdi*_*ing 3 pointers for-loop go goroutine
请检查以下代码段:
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)
}
Run Code Online (Sandbox Code Playgroud)
链接到这里:https://play.golang.org/p/cdryPmyWt5
上面的代码将检查for循环中指针和值之间的差异,而go语句也同时使用.代码:
values := []field{{"one"},{"two"},{"three"}}
for _, v := range values {
go v.print()
}
Run Code Online (Sandbox Code Playgroud)
我们知道控制台将打印三个三三作为结果,因为for循环在goroutines开始执行之前运行到它的末尾,将v写为切片的最后一个元素.但指针怎么样?
poniters := []*field{{"one"},{"two"},{"three"}}
for _, v := range poniters {
go v.print()
}
Run Code Online (Sandbox Code Playgroud)
它似乎打印一两三,为什么?
谢谢.
小智 7
答:在调用函数之前评估参数.在评估它们之后,调用的参数将通过值传递给函数,并且被调用的函数开始执行,因此:
第一go v.print()是语法糖go (*field).print(&v)和
所述第二go v.print()是语法糖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).
而对于第二个,go (*field).print(v)这里v是指针和三个goroutines参数在调用之前被评估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)
}
Run Code Online (Sandbox Code Playgroud)
输出:
use values:
&{one}
&{two}
&{three}
three
three
three
use pointers:
&{one}
&{two}
&{three}
two
one
three
Run Code Online (Sandbox Code Playgroud)
2-在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 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)
}
Run Code Online (Sandbox Code Playgroud)
输出:
use values:
&{one}
one
&{two}
two
&{three}
three
use pointers:
&{one}
&{two}
&{three}
one
two
three
Run Code Online (Sandbox Code Playgroud)
B:你有数据竞争,尝试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)
Run Code Online (Sandbox Code Playgroud)