我试图理解在创建一个带有参数的匿名函数与使该函数充当闭包之间的Go的区别.这是一个区别的例子.
带参数:
func main() {
done := make(chan bool, 1)
go func(c chan bool) {
time.Sleep(50 * time.Millisecond)
c <- true
}(done)
<-done
}
Run Code Online (Sandbox Code Playgroud)
作为封闭:
func main() {
done := make(chan bool, 1)
go func() {
time.Sleep(50 * time.Millisecond)
done <- true
}()
<-done
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,第一种形式何时优于第二种形式?你有没有使用参数来做这种事情?我唯一能看到第一种形式有用的是func(x, y)从另一个函数返回a时.
ANi*_*sus 21
使用闭包与使用函数参数之间的区别与共享相同的变量与获取值的副本有关.考虑下面这两个例子.
在Closure中,所有函数调用都将使用存储的值i.在任何goroutine有时间打印它的值之前,这个值很可能已经达到3.
在参数示例中,每次函数调用都会传递i调用时的值的副本,从而为我们提供了我们更想要的结果:
关闭:
for i := 0; i < 3; i++ {
go func() {
fmt.Println(i)
}()
}
Run Code Online (Sandbox Code Playgroud)
结果:
3
3
3
参数:
for i := 0; i < 3; i++ {
go func(v int) {
fmt.Println(v)
}(i)
}
Run Code Online (Sandbox Code Playgroud)
结果:
0
1
2
游乐场: http ://play.golang.org/p/T5rHrIKrQv
如果您打算更改您不想在函数中观察的变量的值,则绝对首选第一种形式。
当匿名函数位于for循环内并且您打算使用循环的变量时,这是典型情况,例如:
for i := 0; i < 10; i++ {
go func(i int) {
fmt.Println(i)
}(i)
}
Run Code Online (Sandbox Code Playgroud)
如果不传递变量,i您可能会观察到打印10十次。随着路过i,你会看到从印刷的数字0来9。
如果您不想更改该变量的值,则不传递该变量会更便宜,因此不创建该变量的另一个副本。对于大型结构尤其如此。尽管如果以后更改代码并修改变量,则可能很容易忘记检查其对闭包的影响并获得意外的结果。
在某些情况下,您确实希望观察对“外部”变量所做的更改,例如:
func GetRes(name string) (Res, error) {
res, err := somepack.OpenRes(name)
if err != nil {
return nil, err
}
closeres := true
defer func() {
if closeres {
res.Close()
}
}()
// Do other stuff
if err = otherStuff(); err != nil {
return nil, err // res will be closed
}
// Everything went well, return res, but
// res must not be closed, it will be the responsibility of the caller
closeres = false
return res, nil // res will not be closed
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,GetRes()将打开一些资源。但是在返回之前,还必须完成其他可能会失败的事情。如果失败,则res必须关闭且不予退货。如果一切顺利,res切勿关闭并返回。