在循环中使用匿名函数捕获循环变量会出现问题:
func main() {var funcs []func()for i := 0;i < 3; i++ {funcs = append(funcs, func() {fmt.Printf("%d, ", i)})}for _, f := range funcs {f()}}// 3, 3, 3,
修改为一下形式就不会出现问题:
func main() {var funcs []func()for i := 0;i < 3; i++ {ti := i // 赋值给另一个变量funcs = append(funcs, func() {fmt.Printf("%d, ", ti)})}for _, f := range funcs {f()}}// 0, 1, 2,
原因在于匿名函数捕获的是变量的地址,而该地址处的值在循环中多次改变。修改后的版本将其赋值给另一个变量,捕获到的地址不一样。
func main() {funcs := make([]func(), 0)for i := 0; i < 3; i++ {ti := ifuncs = append(funcs, func() {fmt.Printf("%v, %v\n", &i, &ti)})}for _, f := range funcs {f()}}//0x1400001e088, 0x1400001e090//0x1400001e088, 0x1400001e098//0x1400001e088, 0x1400001e0a0
以上问题同样会出现在基于匿名函数的goroutine中。
func main() {const t = 3ch := make(chan struct{})for i := 0; i < t; i++ {go func() {fmt.Printf("%d, ", i)ch <- struct{}{}}()}for i := 0; i < t; i++ {<-ch}}// 3, 3, 3,
在这里顺带提一句,go中的局部变量可能在堆上分配,也可能在栈上分配,由编译器实现,不保证分配在哪。堆变量的gc由运行时完成。
