先看一段代码
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for _, sal := range []string{"a", "b", "c"} {
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println(sal)
}()
}
wg.Wait()
}
第一眼看上去程序很简单啊,就是一个for循环,每次循环时起一个goroutine啊?
那么执行结果应该是什么呢,慢着!goroutine不是立即起的哎!难道是乱序打印a,b,c???
run一下便知!
c
c
c
为何都是c? 难道是巧合?多跑几次试试?
emmmmm,实锤了,就是c!
哈子玩意?
书上这样解释:
goroutine正在运行一个闭包,该闭包使用变量sal时,字符串迭代已经结束。
那goroutine运行时sal从何而来?如果不幸被GC收走怎么办?
这是一个关于如何管理内存的有趣的点。Golang运行时会足够小心的将对变量sal值的引用仍然保留,由内存转移到堆,一便goroutine可以继续访问它。
通常我们计算机上。在任何goroutine开始运行之前,循环就会退出,所以sal会被转移到堆中,即在堆中的sal为字符切片中 最后一个值c,所以通常会看到三次c。编写这个循环的正确方式是将sal的副本传递到闭包中,这样当goroutine运行时,它将从循环的迭代中操作数据。
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for _, sal := range []string{"a", "b", "c"} {
wg.Add(1)
go func(sal string) {
defer wg.Done()
fmt.Println(sal)
}(sal)
}
wg.Wait()
}
c
b
a
这下运行结果就对了(随机打印a,b,c)!