字符串和并发性的不变性

Rud*_*koŭ 8 string concurrency atomic immutability go

我们应该同步写字符串吗?由于字符串是不可变的,因此从2个不同的线程写入和读取之间永远不会出现不一致的状态,对吗?

换句话说,为什么我们没有字符串类型的原子

icz*_*cza 10

string值是不可变的,但变量不是.变量是 - 他们的名字 - 变量,它们的值可以改变.

您不需要同步来访问string无法更改的值.如果一个string值交给你,那个(它的内容string)将始终保持不变(包的unsafe使用不计算).

如果要string同时从多个goroutine 访问类型的变量,如果至少有一个访问是写入(写入更改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)
}
Run Code Online (Sandbox Code Playgroud)

输出(在Go Playground上试试):

First  s = [1 2 3]
Second s = [100 2 3]
Run Code Online (Sandbox Code Playgroud)

专注于sliceTest():它接收切片,然后打印出来.然后等待一点(给一个并行的goroutine"go"来修改它,并等待这个修改完成),然后重新打印它,它已经改变了,但sliceTest()它本身并没有修改它.

现在如果sliceTest()会收到一个string参数,这不可能发生.

请参阅相关/可能重复:不可变字符串和指针地址