我们应该在编写字符串上进行同步吗?因为字符串是不可变的,所以我们永远不会在两个不同线程的读写之间获得不一致的状态,对吗?
换句话说,为什么我们没有字符串类型的原子?
string值是不可变的,但变量不是。变量就是 变量 ,顾名思义, 变量 的值可以更改。
string
您不需要同步就可以访问一个string不变的值。如果将string值传递给您,则该值(的内容string)将始终保持不变(不使用包unsafe)。
unsafe
如果要同时访问string多个goroutine中的类型变量,并且至少其中一次访问是写操作(更改变量值的写操作),则需要进行同步string。对于Go中任何类型的变量都是如此,该string类型在任何方面都不是特殊的。
实际上这是什么意思?
如果您有一个接收string值的函数,"hello"则可以确保该string值将保持"hello"不变。因此,如果您自己不更改参数(例如,不给它分配新值),它将始终保留该string值"hello"。
"hello"
作为反例,如果您的函数收到slice值[]byte{1, 2, 3},则您没有相同的保证,因为slice是可变的。调用方还具有切片值(slice标头),否则它不能首先传递它。而且如果调用者同时修改了slice 的 元素 ,因为它们共享相同的后备数组,则传递给您的slice还将看到更改的数据……具有适当的同步;因为如果没有同步,这将是数据争用(因此是未定义的行为)。
[]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进行“修改”,然后等待此修改完成),然后再次打印它,并且它已更改,但sliceTest()它本身并未对其进行修改。
sliceTest()
现在,如果sliceTest()将string改为接收一个参数,则不会发生这种情况。