Skip to content

Instantly share code, notes, and snippets.

@haozibi
Last active June 3, 2020 11:17
Show Gist options
  • Select an option

  • Save haozibi/33e4283f834ef4e5eea0e213947fd548 to your computer and use it in GitHub Desktop.

Select an option

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)
}
@wedojava
Copy link
Copy Markdown

wedojava commented Jun 3, 2020

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

@haozibi
Copy link
Copy Markdown
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