在 Go 中,闭包 (closures) 是捕获其周围作用域变量的函数。闭包最常用于在其他函数内创建匿名函数。
使用闭包时最常见的问题是,在 goroutine 内部使用循环变量时会出现意想不到的行为:
for i := 0; i < 3; i++ { go func() { fmt.Println(i) }() }
每个 goroutine 可能会打印出相同的值 i,因为变量 i 是循环中的,闭包捕获的正是变量,而不是它在每次迭代中的值。
正确的方法:
for i := 0; i < 3; i++ { go func(val int) { fmt.Println(val) }(i) }
这种行为与闭包持有对变量的引用(地址),而不是它的切片(by value)值有关。
如果多个 goroutine 在循环内捕获循环变量,它们将打印出什么值?
答案: 所有的 goroutine 可能会打印出相同的值(通常是最后一个),因为它们看到的是变量的当前值,而在 goroutine 执行时,循环已经结束。为了避免这种情况,必须将变量作为参数传递给闭包。
示例:
for i := 0; i < 5; i++ { go func() { fmt.Println(i) }() } // 很可能得到:5 5 5 5 5
故事
故事
故事
在快递初创公司中,不正确使用闭包更新订单坐标导致最后一个订单的坐标在切片中被批量更新,而不是当前的——这是由于在匿名函数内部访问切片时发生的竞态。