Skip to content

Instantly share code, notes, and snippets.

@haozibi
Last active June 3, 2020 11:17
Show Gist options
  • Save haozibi/33e4283f834ef4e5eea0e213947fd548 to your computer and use it in GitHub Desktop.
Save haozibi/33e4283f834ef4e5eea0e213947fd548 to your computer and use it in GitHub Desktop.
range 与 闭包的使用,Go词法作用域陷阱,捕获迭代变量 -- gopl 5.6.1
package main
import (
"fmt"
"sync"
)
var list = [...]int{1, 2, 3, 4, 5}
func main() {
A()
B()
}
// **引用**
func A() {
var result []func()
for k, v := range list {
// 正常输出 list 的 kv
fmt.Println("A +", k, v)
func() {
// 匿名函数输出 kv,引用 A 的变量
// 由于 for 没有结束,所以和直接输出没有区别
fmt.Println("A -", k, v)
}()
// 把 v 都添加到 result 中,
// v 具体是引用类型的
result = append(result, func() {
fmt.Println("A #", k, v)
})
}
// for 循环添加的 v 是引用类型的,所以 result 的值都是同一个地址,指向是最后一个
for _, v := range result {
v()
}
var wg sync.WaitGroup
for k, v := range list {
wg.Add(1)
// 把 kv 异步输出,因为匿名函数是**引用**函数的变量
// 运行协程时 for 循环完毕,所以 kv 地址指向的都是最后一个
go func() {
fmt.Println("A *", k, v)
wg.Done()
}()
// 因为协程运行时 for 循环已经完成,所以协程输出都是最后一位的 kv
// 可以增加延时,让 for 循环延时完成,此时协程输出为正确的
// time.Sleep(500 * time.Millisecond)
}
wg.Wait()
}
func B() {
var wg sync.WaitGroup
// for 循环也存在同样的问题,注意协程中 i 的值
for i := 0; i < len(list); i++ {
wg.Add(1)
fmt.Println("B +", i, list[i])
go func() {
fmt.Println("B -", i)
wg.Done()
}()
}
wg.Wait()
}
// output:
// A + 0 1
// A - 0 1
// A + 1 2
// A - 1 2
// A + 2 3
// A - 2 3
// A + 3 4
// A - 3 4
// A + 4 5
// A - 4 5
// A # 4 5
// A # 4 5
// A # 4 5
// A # 4 5
// A # 4 5
// A * 4 5
// A * 4 5
// A * 4 5
// A * 4 5
// A * 4 5
// B + 0 1
// B + 1 2
// B + 2 3
// B + 3 4
// B + 4 5
// B - 5
// B - 5
// B - 5
// B - 5
// B - 5
//
package main
import (
"fmt"
"time"
)
func main() {
// fmt.Println(foo())
l := []int{1, 2, 3, 4}
for k, v := range l {
// range 会逐个拷贝到 v(只初始化一次,每次赋值),协程内读到的都是同一个 v
// 闭包的时候,使用的变量为外面变量的**引用**
// 所以协程读到的都是同一个地址(都是 v),所以读到的都是 v 最后的值
go func() {
fmt.Println(k, v, &v)
}()
}
time.Sleep(time.Second)
}
@haozibi
Copy link
Author

haozibi commented Jun 3, 2020

非常有帮助,感谢分析,这一部分内容真是细节中的细节啊。

thx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment