在Go语言中,闭包(closure)是指一个函数值(function value)引用了其函数体之外的变量。闭包可以捕获和存储这些变量的引用,即使在函数执行时这些变量已经超出了其原始作用域。
当你在循环中使用匿名函数(闭包)时,如果这个匿名函数引用了循环变量,那么所有匿名函数都会共享同一个循环变量的引用。由于Go语言中的循环变量在每次迭代时都会被更新,因此所有闭包都会捕获到循环变量的最终值。
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 3; i++ {
go func() {
fmt.Println(i)
}()
}
time.Sleep(time.Second) // 等待goroutine执行
}
3
3
3
在上面的代码中,i
是循环变量,所有的匿名函数都捕获了同一个 i
的引用。当循环结束时,i
的值变为 3
,此时所有的匿名函数才开始执行,因此它们都会输出 3
。
为了避免这个问题,你可以在每次迭代时创建一个新的局部变量,并将循环变量的值赋给这个局部变量。这样每个闭包都会捕获到不同的变量引用。
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 3; i++ {
i := i // 创建一个新的局部变量
go func() {
fmt.Println(i)
}()
}
time.Sleep(time.Second) // 等待goroutine执行
}
0
1
2
在修改后的代码中,i := i
创建了一个新的局部变量 i
,每个迭代中的匿名函数都捕获了这个局部变量的引用。因此,每个闭包都会输出对应的迭代值。
在Go语言中,循环内的匿名函数(闭包)如果引用了循环变量,会捕获该变量的引用,而不是值。为了避免所有闭包都输出循环变量的最终值,可以在每次迭代时创建一个新的局部变量,并将循环变量的值赋给这个局部变量。这样每个闭包都会捕获到不同的变量引用,从而输出正确的值。