golang踩坑 2.goroutine闭包和遍历字符串字符

简介: golang踩坑 2.goroutine闭包和遍历字符串字符

我们都生活在阴沟里,但仍有人仰望星空。——奥斯卡王尔德


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

相关文章
|
3月前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
100 4
|
3月前
|
Go 调度
Golang语言goroutine协程篇
这篇文章是关于Go语言goroutine协程的详细教程,涵盖了并发编程的常见术语、goroutine的创建和调度、使用sync.WaitGroup控制协程退出以及如何通过GOMAXPROCS设置程序并发时占用的CPU逻辑核心数。
72 4
Golang语言goroutine协程篇
|
3月前
|
Go
Golang语言基本数据类型默认值及字符串之间互相转换案例
这篇文章讲解了Golang语言中基本数据类型的默认值、类型转换的概述以及整型、浮点型、字符串之间的相互转换案例,包括如何将基本数据类型转换为字符串类型和字符串类型转换为基本数据类型,以及字符串与字节切片之间的转换。
33 2
|
3月前
|
Go
Golang语言基础数据类型之字符串常用的操作
这篇文章介绍了Golang语言中字符串的定义、常用操作,包括字符串长度查看、遍历、类型转换、子串统计、比较、查找位置、替换、切割、大小写转换、剔除字符、前缀后缀判断、拼接、子串包含判断以及字符串join操作,同时提供了官方文档的查看方法。
31 1
|
4月前
|
Go
[golang]字符串拼接
[golang]字符串拼接
|
4月前
|
存储 程序员 编译器
Golang 中的字符串:常见错误和最佳实践
Golang 中的字符串:常见错误和最佳实践
|
3月前
|
Go
Golang语言基础数据类型之字符类型
这篇文章介绍了Go语言中的字符类型,包括字符概述、byte和rune类型的定义、转义字符的使用以及如何遍历字符串获取字符的示例。
19 0
|
4月前
|
Go
golang对遍历目录操作的优化
【8月更文挑战第7天】在Golang中优化目录遍历能提升性能。可通过缓冲读取减少系统调用、使用协程并发处理大量文件、按需跳过不必要目录及仅获取所需文件信息等方式实现。示例代码展示了如何运用协程并行遍历子目录以加快处理速度。实际应用时需依据场景选择合适策略。
|
4月前
|
Go 开发者
|
4月前
|
NoSQL Unix 编译器
Golang协程goroutine的调度与状态变迁分析
文章深入分析了Golang中goroutine的调度和状态变迁,包括Grunnable、Gwaiting、Grunning和Gsyscall等状态,以及它们之间的转换条件和原理,帮助理解Go调度器的内部机制。
52 0