string 值是不可变的,但变量不是。变量是——正如他们的名字所说的——变量,它们的值可以改变。
访问string 值不需要同步,这是无法更改的。如果将string 值传递给您,那么(string 的内容)将始终保持不变(不计算包unsafe 的使用)。
当你想同时从多个 goroutine 访问 string 类型的变量时,如果至少有一个访问是写入(更改 string 变量的值的写入),则需要同步。这对于 Go 中任何类型的变量都是如此,string 类型在任何方面都不是特殊的。
这在实践中意味着什么?
如果您有一个接收string 值"hello" 的函数,则可以确定string 值无论如何都会保持"hello"。因此,如果您不自己更改参数(例如,您不为其分配新值),它将始终保持 string 值 "hello"。
作为反例,如果您的函数接收到切片值[]byte{1, 2, 3},则您没有相同的保证,因为切片是可变的。调用者也有切片值(切片标头),否则它一开始就无法传递它。如果调用者同时修改了切片的元素,因为它们共享同一个后备数组,那么交给你的切片也将看到更改的数据……并具有适当的同步;因为如果没有同步,这将是一场数据竞争(因此是未定义的行为)。
看这个例子:
var sig = make(chan int)
func main() {
s := []byte{1, 2, 3}
go func() {
<-sig
s[0] = 100
sig <- 0
}()
sliceTest(s)
}
func sliceTest(s []byte) {
fmt.Println("First s =", s)
sig <- 0 // send signal to modify now
<-sig // Wait for modification to complete
fmt.Println("Second s =", s)
}
输出(在Go Playground 上试试):
First s = [1 2 3]
Second s = [100 2 3]
关注sliceTest():它接收一个切片,然后打印它。然后稍等一下(给一个并发的 goroutine 一个“go”来修改它,并等待这个修改完成),并再次打印它,它已经改变了,但 sliceTest() 本身并没有修改它。
现在,如果 sliceTest() 将收到 string 参数,则不会发生这种情况。
查看相关/可能的重复:Immutable string and pointer address