Go语言中闭包通过引用捕获外部变量,使变量逃逸到堆上以延长生命周期。例如counter函数中的count被闭包持续引用并修改。多个闭包共享同一变量时,操作的是同一内存地址。循环中若未注意,所有闭包可能捕获同一个i实例,导致意外结果。

Go语言中的闭包通过引用方式捕获外部作用域的变量,而不是值拷贝。这意味着闭包内部访问的是外部变量本身的内存地址,因此可以读取和修改这些变量的值。
当一个匿名函数引用了其外层函数的局部变量时,Go会将这些变量从栈上“逃逸”到堆上,确保它们在外部函数返回后依然有效。这是闭包能够持续访问这些变量的关键。
例如:
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
这里的 count 原本是 counter 函数的局部变量,但由于被内部匿名函数引用,它会被分配到堆上。每次调用返回的函数时,都是在操作同一个 count 变量的引用。
立即学习“go语言免费学习笔记(深入)”;
如果多个闭包捕获了同一个外部变量,它们实际上共享对该变量的引用。一个闭包对变量的修改,会影响其他闭包的读取结果。
func example() {
x := 10
inc := func() { x++ }
dec := func() { x-- }
inc()
fmt.Println(x) // 输出 11
dec()
fmt.Println(x) // 输出 10
}
inc 和 dec 都捕获了同一个 x,它们的操作作用于同一内存位置。
在 for 循环中创建闭包时,如果不小心,所有闭包可能捕获的是同一个变量实例,而不是每次迭代的副本。
错误示例:
for i := 0; i < 3; i++ {
go func() {
fmt.Println(i)
}()
}
这三个 goroutine 很可能都打印出 3,因为它们共享同一个 i 变量。当 goroutine 实际执行时,循环可能已经结束,i 的值为 3。
正确做法是通过参数传值:
for i := 0; i < 3; i++ {
go func(val int) {
fmt.Println(val)
}(i)
}
或者在循环内创建局部副本:
for i := 0; i < 3; i++ {
i := i // 创建新的局部变量
go func() {
fmt.Println(i)
}()
}
基本上就这些。Go 的闭包捕获的是变量的引用,理解这一点就能避免大多数常见问题。关键是意识到变量是否被“逃逸”到堆上,以及多个闭包是否共享状态。
以上就是Golang的闭包(closure)是如何捕获外部变量的的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号