您的位置 首页 golang

Go避坑指南(一):for-range 可变性

在Go并发编程中,我们经常会将运行的中间数据放到 chan 里面,然后启动另外一个协程从chan 里面获取数据,如下所示:

 package main

import (
	"fmt"
	"sync"
)

type A struct {
	id int
}

func main() {
	channel := make(chan A, 5)
	
	var wg sync.WaitGroup
	
	wg.Add(1)
	go func() {
		defer wg.Done()
		for a := range channel { // 读 chan
			wg.Add(1)
			go func() {
				defer wg.Done()
				fmt.Println(a.id)
			}()
		}

	}()
	
	for i := 0; i < 10; i++ {
		channel <- A{id:i} // 将数据放到chan
	}
	close(channel)
	
	wg.Wait()
}  

我们希望得到的结果打印从 0 到 9 这几个数字,但真实的结果却是

 6
6
6
6
6
9
9
9
9
9   

这是因为go的for range 的遍历中间变量 a 其实是共享同一个地址空间的,也就是说每次for循环遍历的时候他们读的是同一个变量。所以才会导致上面的现象。解决这个问题的方法就是复制这个变量的值,譬如

 go func() {
	defer wg.Done()
	for a := range channel {
		wg.Add(1)
		go func(item A) {
			defer wg.Done()
			fmt.Println(item.id)
		}(a) // 变量复制
	}
}()  

我们还可以go 协程之外去捕获复制这个变量也是可以的。

 for a := range channel {
	wg.Add(1)
	item := a // 变量复制
	go func() {
		defer wg.Done()
		fmt.Println(item.id)
	}()
}  

文章来源:智云一二三科技

文章标题:Go避坑指南(一):for-range 可变性

文章地址:https://www.zhihuclub.com/86286.shtml

关于作者: 智云科技

热门文章

网站地图