我们都生活在阴沟里,但仍有人仰望星空。——奥斯卡王尔德
1. 前言
这篇文章我们来聊聊在循环中使用Goroutine中捕获参数的问题和使用下标获取字符串的字符问题,这两个问题在项目中比较常见,大家记得要规避。
2. Goroutine中捕获参数
goroutine中捕获的循环变量, 都为循环最后的值。
func main() {
for i, v := range []string{"a", "b", "c", "d", "e"} {
// goroutine中捕获循环变量
go func() {
fmt.Printf("index: %v, value: %v\n", i, v)
}()
}
// 此处应该使用waitgroup实现, 为了简单使用了sleep
time.Sleep(1 * time.Second)
}
//================输出==============
index: 4, value: e
index: 4, value: e
index: 4, value: e
index: 4, value: e
index: 4, value: e
原因:
goroutine中捕获的不是"值", 而是"有地址的变量". for循环可能会先结束, 之后各个goroutine才开始执行. 因此得到的是变量的最终值。
避免方式 在goroutine启动的函数中, 把变量作为参数捕获。
func main() {
for i, v := range []string{"a", "b", "c", "d", "e"} {
// 把循环变量作为参数传入
go func(i int, v string) {
// i, v是函数内部的局部变量
fmt.Printf("index: %v, value: %v\n", i, v)
}(i, v)
}
time.Sleep(1 * time.Second)
}
//================输出==============
index: 0, value: a
index: 1, value: b
index: 4, value: e
index: 3, value: d
index: 2, value: c
3. 获取字符串的字符
使用下标获取字符串的字符时, 可能得到奇怪的字符
func main() {
s := "hello"
fmt.Printf("%c\n", s[1])
s = "你好"
fmt.Printf("%c\n", s[1])
}
//============输出===========
e
½
原因:
golang是以utf8格式保存字符串的, 字符串的下标操作, 访问的是字节, 而不是字符. len函数输出的也是字节数, 如len("hello")==5, len("你好")==6。
避免方式 把字符串转化为[]rune/[]int32, 或者使用range遍历
func main() {
s := "你好"
// 强转为[]rune
fmt.Printf("%c\n", []rune(s)[1])
fmt.Println()
// 使用range遍历
for _, c := range s {
fmt.Printf("%c\n", c)
}
}
//===============输出=================
好
你
好
4. 小结
针对循环创建goroutine获取外面参数这种闭包问题记得一定要小心,根据实际情况做出调整。当然循环根据下标获取字符串字符也会有问题,一定小心谨慎对待。
5. 关注公众号
微信公众号:堆栈future
希望大家关注哈,原创不容易,求点赞,求关注,求分享