看到这样一段代码,有大佬给解释下pointer receiver
对 goroutine 执行的影响,或者说解释下这段代码三个函数表现行为的具体原因,谢谢了🙏
playground 地址: https://play.golang.org/p/cKrC0oy7FP
package main
import (
"fmt"
"time"
)
type field struct {
name string
}
func (p *field) print() {
fmt.Println(p.name)
}
func TestClosure() {
data := []*field{{"one"}, {"two"}, {"three"}}
for _, v := range data {
go v.print()
}
time.Sleep(3 * time.Second)
}
func TestClosure1() {
data := []field{{"one"}, {"two"}, {"three"}}
for _, v := range data {
go v.print()
}
time.Sleep(3 * time.Second)
}
func TestClosure2() {
data := []*field{{"one"}, {"two"}, {"three"}}
for _, v := range data {
go func(){
v.print()
}()
}
time.Sleep(3 * time.Second)
}
func main(){
TestClosure()
fmt.Println("----")
TestClosure1()
fmt.Println("----")
TestClosure2()
}
1
wqlin 2018-10-26 20:52:58 +08:00 1
首先看 TestClosure2 ; 犯了常见错误,可以参考: https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables
应该改成: ``` func TestClosure2() { data := []*field{{"one"}, {"two"}, {"three"}} for _, v := range data { go func(v *field){ v.print() }(v) } time.Sleep(3 * time.Second) } ``` 理解了 TestClosure2 之后来看 TestClose1。这里要理解 function receiver ;可以参考: https://tour.golang.org/methods/4。关键是这句话 > With a value receiver, the Scale method operates on a copy of the original Vertex value. (This is the same behavior as for any other function argument.) > 所以要实现同样的效果,可以改成: ``` func TestClosure1() { data := []field{{"one"}, {"two"}, {"three"}} for _, v := range data { go func(v field){ v.print() }(v) } time.Sleep(3 * time.Second) } ``` 最后放 playground: https://play.golang.org/p/2C71XfJm2SI |
2
wqlin 2018-10-26 21:00:48 +08:00
@wqlin #1
TestClosure1 也可以改成: ``` func TestClosure1() { data := []field{{"one"}, {"two"}, {"three"}} for i := range data { go data[i].print() } time.Sleep(3 * time.Second) } ``` 实现同样的效果 |
3
icexin 2018-10-26 21:35:24 +08:00 1
代码改成这样你就能明白了
``` go package main import ( "fmt" "time" ) type field struct { name string } func print(p *field) { fmt.Println(p.name) } func TestClosure() { data := []*field{{"one"}, {"two"}, {"three"}} for _, v := range data { go print(v) } time.Sleep(3 * time.Second) } func TestClosure1() { data := []field{{"one"}, {"two"}, {"three"}} for _, v := range data { go print(&v) } time.Sleep(3 * time.Second) } func TestClosure2() { data := []*field{{"one"}, {"two"}, {"three"}} for _, v := range data { go func(){ print(v) }() } time.Sleep(3 * time.Second) } func main(){ TestClosure() fmt.Println("----") TestClosure1() fmt.Println("----") TestClosure2() } ``` `go print(v)`会在执行函数前把参数(v)以值拷贝的方式固定住 `go func(){print(v)}` 会在执行的时候再获取参数(v)的值 |
4
reus 2018-10-26 21:35:46 +08:00
for 语句的循环变量,需要传入闭包时,应该先复制一份,因为每次循环都是使用同一个变量的
加 v := v 就可以复制了 |
5
cloudyplain 2018-10-27 07:59:01 +08:00
TestClosure,指针
TestClosure1,v.print() 等价 (&v).print,print 使用的是相同的地址,但指针内容在循环后被改变 TestClosure2, 闭包常见问题 |
6
Appla 2018-10-27 13:35:27 +08:00
https://golang.org/ref/spec#Go_statements
``` The function value and parameters are evaluated as usual in the calling goroutine, but unlike with a regular call, program execution does not wait for the invoked function to complete. ``` 试试把 time.Sleep(1 * time.Second)加到执行 go 语句的后面看看 |