Last active
June 3, 2020 11:17
-
-
Save haozibi/33e4283f834ef4e5eea0e213947fd548 to your computer and use it in GitHub Desktop.
range 与 闭包的使用,Go词法作用域陷阱,捕获迭代变量 -- gopl 5.6.1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
// |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
thx