GO Rountine 闭包 问题

1,255 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

GO Rountine 闭包 问题

case1

看下面代码,可以猜测下输出结果是啥?

a := []int{1, 2, 3}
	for _, cur := range a {
		go func() {
			doPrint(cur)
		}()
	}

执行结果:

=== RUN   TestSlice
3
3
3
--- PASS: TestSlice (3.00s)
PASS

发现会有问题,为啥每次打印的都是3, 在循环中 cur 是一个单变量,表示每个数组元素的元素值,闭包都只绑定到一个变量,因为协程可能在循环结束后还没有开始执行。因此每个循环都是最后一个值。

可在for循环代码块内把当前迭代的变量值保存到一个局部变量中 改成如下即可:

a := []int{1, 2, 3}
	for _, cur := range a {
    v := cur
		go func() {
			doPrint(v)
		}()
	}

case 2

一个解决方法是把当前的迭代变量作为匿名goroutine的参数。

func TestSlice(t *testing.T) {
	a := []int{1, 2, 3}
	for _, cur := range a {
		go func(d int) {
			doPrint(d)
		}(cur)
	}

执行结果:

=== RUN   TestSlice
3
1
2
--- PASS: TestSlice (3.00s)
PASS

case 3

a := []int{1, 2, 3}
	for _, cur := range a {
		go doPrint(cur)
	}

执行结果:

=== RUN   TestSlice
3
1
2
--- PASS: TestSlice (3.00s)
PASS

为啥这个结果不是和第一个一致的呢?其实这个 case3 和 case2 是一个意思,都是通过匿名函数的方式传承。调用每个闭包是将 cur 作为参数传递给闭包。cur 在每次循环时都被重新赋值,并将每个协程的 cur 放置在栈中,所以当协程最终被执行时,每个索引值对协程都是可用的,会输出不同的值。

附赠:Go 基础类型

类型长度(字节)默认值说明
bool1false
byte10uint8
rune40Unicode Code Point, int32
int, uint4或8032 或 64 位
int8, uint810-128 ~ 127, 0 ~ 255,byte是uint8 的别名
int16, uint1620-32768 ~ 32767, 0 ~ 65535
int32, uint3240-21亿~ 21亿, 0 ~ 42亿,rune是int32 的别名
int64, uint6480
float3240.0
float6480.0
complex648
complex12816
uintptr4或8以存储指针的 uint32 或 uint64 整数
array值类型
struct值类型
string“”UTF-8 字符串
slicenil引用类型
mapnil引用类型
channelnil引用类型
interfacenil接口
functionnil函数