携手创作,共同成长!这是我参与「掘金日新计划 · 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 基础类型
类型 | 长度(字节) | 默认值 | 说明 |
---|---|---|---|
bool | 1 | false | |
byte | 1 | 0 | uint8 |
rune | 4 | 0 | Unicode Code Point, int32 |
int, uint | 4或8 | 0 | 32 或 64 位 |
int8, uint8 | 1 | 0 | -128 ~ 127, 0 ~ 255,byte是uint8 的别名 |
int16, uint16 | 2 | 0 | -32768 ~ 32767, 0 ~ 65535 |
int32, uint32 | 4 | 0 | -21亿~ 21亿, 0 ~ 42亿,rune是int32 的别名 |
int64, uint64 | 8 | 0 | |
float32 | 4 | 0.0 | |
float64 | 8 | 0.0 | |
complex64 | 8 | ||
complex128 | 16 | ||
uintptr | 4或8 | 以存储指针的 uint32 或 uint64 整数 | |
array | 值类型 | ||
struct | 值类型 | ||
string | “” | UTF-8 字符串 | |
slice | nil | 引用类型 | |
map | nil | 引用类型 | |
channel | nil | 引用类型 | |
interface | nil | 接口 | |
function | nil | 函数 |