1. Golang

Golang并发之道-for循环中的goroutine

先看一段代码

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)!