for-range 循环中指针和值切片之间的区别
differences between pointer and value slice in for-range loop
请检查这段代码:
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)
}
Link 这里:https://play.golang.org/p/cdryPmyWt5
上面的代码是在for循环中检查指针和值的区别,同时也用到了go语句。对于代码:
values := []field{{"one"},{"two"},{"three"}}
for _, v := range values {
go v.print()
}
我们知道控制台将打印 三三三 作为结果,因为 for 循环在 goroutines 开始执行之前就结束了,goroutines 开始执行,将 v 作为切片的最后一个元素.但是指针呢?
poniters := []*field{{"one"},{"two"},{"three"}}
for _, v := range poniters {
go v.print()
}
好像打印了一二三,为什么?
谢谢。
A: 在调用函数之前评估参数。在对它们求值后,调用的参数按值传递给函数,被调用的函数开始执行,所以:
第一个go v.print()
是go (*field).print(&v)
和
的语法糖
第二个 go v.print()
是 go (*field).print(v)
.
的语法糖
如果第一个 for
循环在 goroutines 开始之前完成,&v
调用是相同的,这三个调用都是相同的。通过在第一个循环的 go v.print()
之后添加 time.Sleep(100)
来查看代码 2。或者在 The Go Playground (Code 3 with sync.WaitGroup
).
上使用 go func(v field) { v.print() }(v)
另外,这里有 data race(见 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)
}
输出:
use values:
&{one}
&{two}
&{three}
three
three
three
use pointers:
&{one}
&{two}
&{three}
two
one
three
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)
}
输出:
use values:
&{one}
one
&{two}
two
&{three}
three
use pointers:
&{one}
&{two}
&{three}
one
two
three
B:你有数据竞争,尝试 go build -race
your code,然后 运行 生成文件,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)
请检查这段代码:
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)
}
Link 这里:https://play.golang.org/p/cdryPmyWt5
上面的代码是在for循环中检查指针和值的区别,同时也用到了go语句。对于代码:
values := []field{{"one"},{"two"},{"three"}}
for _, v := range values {
go v.print()
}
我们知道控制台将打印 三三三 作为结果,因为 for 循环在 goroutines 开始执行之前就结束了,goroutines 开始执行,将 v 作为切片的最后一个元素.但是指针呢?
poniters := []*field{{"one"},{"two"},{"three"}}
for _, v := range poniters {
go v.print()
}
好像打印了一二三,为什么?
谢谢。
A: 在调用函数之前评估参数。在对它们求值后,调用的参数按值传递给函数,被调用的函数开始执行,所以:
第一个go v.print()
是go (*field).print(&v)
和
的语法糖
第二个 go v.print()
是 go (*field).print(v)
.
如果第一个 for
循环在 goroutines 开始之前完成,&v
调用是相同的,这三个调用都是相同的。通过在第一个循环的 go v.print()
之后添加 time.Sleep(100)
来查看代码 2。或者在 The Go Playground (Code 3 with sync.WaitGroup
).
上使用 go func(v field) { v.print() }(v)
另外,这里有 data race(见 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)
}
输出:
use values:
&{one}
&{two}
&{three}
three
three
three
use pointers:
&{one}
&{two}
&{three}
two
one
three
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)
}
输出:
use values:
&{one}
one
&{two}
two
&{three}
three
use pointers:
&{one}
&{two}
&{three}
one
two
three
B:你有数据竞争,尝试 go build -race
your code,然后 运行 生成文件,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)