小编典典

字符串和并发性的不变性

go

我们应该在编写字符串上进行同步吗?因为字符串是不可变的,所以我们永远不会在两个不同线程的读写之间获得不一致的状态,对吗?

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


阅读 193

收藏
2020-07-02

共1个答案

小编典典

string值是不可变的,但变量不是。变量就是 变量 ,顾名思义, 变量 的值可以更改。

您不需要同步就可以访问一个string不变的值。如果将string值传递给您,则该值(的内容string)将始终保持不变(不使用包unsafe)。

如果要同时访问string多个goroutine中的类型变量,并且至少其中一次访问是写操作(更改变量值的写操作),则需要进行同步string。对于Go中任何类型的变量都是如此,该string类型在任何方面都不是特殊的。

实际上这是什么意思?

如果您有一个接收string值的函数,"hello"则可以确保该string值将保持"hello"不变。因此,如果您自己不更改参数(例如,不给它分配新值),它将始终保留该string"hello"

作为反例,如果您的函数收到slice值[]byte{1, 2, 3},则您没有相同的保证,因为slice是可变的。调用方还具有切片值(slice标头),否则它不能首先传递它。而且如果调用者同时修改了slice 的
元素 ,因为它们共享相同的后备数组,则传递给您的slice还将看到更改的数据……具有适当的同步;因为如果没有同步,这将是数据争用(因此是未定义的行为)。

请参阅以下示例:

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()string改为接收一个参数,则不会发生这种情况。

2020-07-02