Golang - string 是否线程安全

线程安全是什么

线程安全是指在多线程环境下,程序的执行能够正确地处理多个线程并发访问共享数据的情况,保证程序的正确性和可靠性。

type StringHeader struct {
   Data uintptr  //存放指针,其指向具体的存储数据的内存区域
   Len  int     //字符串的长度
}

并发访问

复制代码
package main
 
import (
 "fmt"
 "sync"
)
 
func main() {
 var wg sync.WaitGroup
 str := "脑子进煎鱼了"
 for i := 0; i < 5; i++ {
  wg.Add(1)
  go func() {
   defer wg.Done()
   fmt.Println(str)
  }()
 }
 wg.Wait()
}
复制代码

定义了一个 string 变量 str,然后启动了 5 个 goroutine,每个 goroutine 都会输出 str 的值。由于 str 是不可变类型,因此在多个 goroutine 中并发访问它是安全的。

不可变类型,指的是一种不能被修改的数据类型,也称为值类型(value type)。不可变类型在创建后其值不能被改变,任何对它的修改操作都会返回一个新的值,而不会改变原有的值。

并发写入

复制代码
func main() {
 var wg sync.WaitGroup
 str := "脑子进煎鱼了"
 for i := 0; i < 5; i++ {
  wg.Add(1)
  go func() {
   defer wg.Done()
   str += "" // 修改 str 变量
   fmt.Println(str)
  }()
 }
 wg.Wait()
}
复制代码

 

 

在每个 goroutine 中向 str 变量中添加了一个感叹号。由于多个 goroutine 同时修改了 str 变量,因此可能会出现数据竞争的情况。

会发现程序输出结果会出现乱序或不一致的情况,可以确认 string 类型变量在多个 goroutine 中是不安全的。

string 实现线程安全

 使用互斥锁(Mutex)来保护共享变量,确保同一时间只有一个goroutine 可以访问(互斥锁会带来一些性能上的开销)

复制代码
package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    var mu sync.Mutex // 定义一个互斥锁
    str := "煎鱼"
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            mu.Lock() // 加锁
            str += ""
            fmt.Println(str)
            mu.Unlock() // 解锁
        }()
    }
    wg.Wait()
}
复制代码

注意:使用 atomic 包满足不了需求【原子操作通常用于对基本类型(如整数、布尔值、指针等)的原子更改,而不是对字符串的修改】

复制代码
package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

func main() {
    var wg sync.WaitGroup
    var str atomic.Value // 定义一个原子变量
    str.Store("hello, world")
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            oldStr := str.Load().(string) // 读取原子变量的值
            newStr := oldStr + "!"
            str.Store(newStr) // 写入原子变量的值
            fmt.Println(newStr)
        }()
    }
    wg.Wait()
}
复制代码

这样子也可以保证 string 类型变量的原子操作。但在现实场景下,仍然无法解决多 goroutine 导致的竞态条件(race condition)。

也就是存在多个 goroutine 并发取到的变量值都是一样的,得到的结果还是不固定的,最终还是要用 Mutex 或者 RWMutex 锁来做共享变量保护。

修改字符串

要修改字符串,可先将其转换成 []rune 或 []byte,完成后再转换为 string。⽆论哪种转换,都会重新分配内存,并复制字节数组。

    test := "abcd"
    runes := []rune(test)
    runes[0] = ''
    demo := string(runes)
    fmt.Printf("%s\n", demo) // 中bcd
posted @   李若盛开  阅读(166)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示