go并发之捕获迭代变量

对于go语言来说,下面这个问题可谓是非常经典了。下面代码会打印出什么结果?

func main() {
	for i := 0; i < 10; i++ {
		go func() {
			fmt.Println(i)
		}()
	}
}  

 
// 这是我的结果
7
7
7
7
7
7
10
10

  

通过结果我们看到。第一:打印的数字不足10个,这个问题我们不在这里讨论。第二:打印出来的数字并不是0~9,而是有重复的,今天我们主要关注这个问题,大家可能都知道这种情况,今天我们来好好聊聊为什么会在这样。

要解释上述问题,第一我们要了解go变量的作用域:go的变量作用域在一个个块中(block),一个花括号就是一个块,比如函数、if语句,for语句都有花括号,甚至直接写一个花括号就可以验证。下面代码是会有两个问题:1.变量a定义了却没有使用。2.fmt.Println找不到变量a

func main() {
	{
		var a int = 1
	}
	fmt.Println(a)
}

 第二我们要了解go中的闭包,在最初的问题中我们就是用func匿名函数实现了一个闭包,而且这个闭包函数直接使用了闭包所在的作用域中的变量i。而这个i的内存地址和for循环中的i是同一个内存地址,随着迭代的进行变量i的内存地址里面存放的数组也在发生了变化,导致打印出来的数值不是期望的0~9,可以通过捕获迭代变量的方法来达到我们预期的效果,上代码:

func main() {
	for i := 0; i < 10; i++ {
		a := i
		go func() {
			fmt.Println(a)
		}()
	}
}

  这里我们用了一个变量a来存放变量i中的数值,每次迭代时a变量会重新申请内存地址用来存放i的值,这样每个闭包中的a在内存中就是各自独立的变量,不会出现打印相同值了。

  当然还有一种处理方式,将i以变量的方式传入函数,也与变量的作用域有关,每个a值都是内存地址独立的变量:

func main() {
	for i := 0; i < 10; i++ {
		go func(a int) {
			fmt.Println(a)
		}(i)
	}
}

  

 

posted @ 2021-07-09 17:04  Cgj20060102030405  阅读(267)  评论(0)    收藏  举报