插件窝 干货文章 Go语言闭包:为什么循环中创建的匿名函数总是输出相同的值?

Go语言闭包:为什么循环中创建的匿名函数总是输出相同的值?

变量 函数 捕获 匿名 753    来源:    2025-03-14

在Go语言中,闭包(closure)是指一个函数值(function value)捕获了其外部作用域中的变量。当你在循环中创建匿名函数并使用闭包时,可能会遇到一个常见的问题:所有匿名函数都捕获了同一个变量,导致它们最终输出的值相同。

问题示例

package main

import (
    "fmt"
    "time"
)

func main() {
    for i := 0; i < 3; i++ {
        go func() {
            fmt.Println(i)
        }()
    }
    time.Sleep(time.Second) // 等待goroutine执行
}

在这个例子中,你可能会期望输出 0, 1, 2,但实际上输出的可能是 3, 3, 3

原因分析

  1. 变量捕获:在Go语言中,闭包捕获的是变量的引用,而不是变量的值。在上面的代码中,所有的匿名函数都捕获了同一个变量 i

  2. 循环执行:当循环执行时,i 的值会不断变化。当循环结束后,i 的值变为 3。此时,所有的匿名函数才开始执行,它们都会输出 i 的最终值 3

解决方案

要解决这个问题,你需要在每次循环时创建一个新的变量,确保每个匿名函数捕获的是不同的变量。

方法1:使用局部变量

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执行
}

在这个例子中,i := i 创建了一个新的局部变量 i,每个匿名函数捕获的是不同的 i,因此输出会是 0, 1, 2

方法2:将变量作为参数传递给匿名函数

package main

import (
    "fmt"
    "time"
)

func main() {
    for i := 0; i < 3; i++ {
        go func(i int) {
            fmt.Println(i)
        }(i) // 将i作为参数传递给匿名函数
    }
    time.Sleep(time.Second) // 等待goroutine执行
}

在这个例子中,i 作为参数传递给匿名函数,每个匿名函数都有自己的 i 副本,因此输出会是 0, 1, 2

总结

在Go语言中,循环中创建的匿名函数如果捕获了循环变量,可能会导致所有函数输出相同的值。为了避免这个问题,可以通过创建局部变量或将变量作为参数传递给匿名函数来确保每个函数捕获的是不同的变量。