String 字符串和并发的不变性

String 字符串和并发的不变性,string,go,concurrency,immutability,atomic,String,Go,Concurrency,Immutability,Atomic,我们应该在写字符串时同步吗?因为字符串是不可变的,所以从两个不同线程的读写之间永远不会出现不一致的状态,对吗 换句话说,为什么我们没有字符串类型 string值是不可变的,但变量不是。变量是–顾名思义–变量,其值可以更改 访问不能更改的字符串值不需要同步。如果将字符串值交给您,则该值(字符串的内容)将始终保持不变(使用不安全的包装不算在内) 如果要同时从多个goroutine访问string类型的变量,则需要同步,前提是至少有一个访问是写入(一个更改string变量值的写入)。这对于Go中任何类

我们应该在写字符串时同步吗?因为字符串是不可变的,所以从两个不同线程的读写之间永远不会出现不一致的状态,对吗


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

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

访问不能更改的
字符串
值不需要同步。如果将
字符串
值交给您,则该值(字符串的内容)将始终保持不变(使用不安全的
包装不算在内)

如果要同时从多个goroutine访问
string
类型的变量,则需要同步,前提是至少有一个访问是写入(一个更改
string
变量值的写入)。这对于Go中任何类型的变量都是正确的,
string
类型在任何方面都不是特殊的

这在实践中意味着什么

如果您有一个函数接收一个
字符串
“hello”
,您可以确保无论发生什么情况,
字符串
值都将保持
“hello”
。因此,如果您自己不更改参数(例如,您没有为其指定新值),它将始终保留
字符串
“hello”

作为一个反例,如果您的函数接收到一个切片值
[]字节{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)
}
关注
sliceTest()
:它接收一个切片并打印它。然后等待一点(让一个并发goroutine“去”修改它,并等待这个修改完成),然后再次打印它,它已经改变了,但是
sliceTest()
本身没有修改它

现在,如果
sliceTest()
将接收一个
string
参数,则不会发生这种情况

请参阅相关/可能的副本:

我们应该在写字符串时同步吗?因为字符串是不可变的,所以我们 将永远不会在2的写入和读取之间获得不一致的状态 不同的线程,对吗

这就是问题所在。答案是:写字符串时同步。很明显,
string
变量是可变的,
string
内容是不可变的,我前面已经解释过了。重申:


Go编译器将强制执行
字符串
内容的不变性。比如说,

package main

func main() {
    var s string = "abc"
    s[0] = '0'
}
package main

import "fmt"

func main() {
    s := "abc"
    fmt.Println(s)
    s = "xyz"
    fmt.Println(s)

}
// error: cannot assign to s[0]
s[0] = '0'
输出:

5:7: cannot assign to s[0]
$ go run -race racer.go
==================
WARNING: DATA RACE
Write at 0x00000050d360 by goroutine 6:
  main.main.func2()
      /home/peter/src/racer.go:15 +0x3a

Previous write at 0x00000050d360 by goroutine 5:
  main.main.func1()
      /home/peter/src/racer.go:10 +0x3a

Goroutine 6 (running) created at:
  main.main()
      /home/peter/src/racer.go:13 +0x5a

Goroutine 5 (running) created at:
  main.main()
      /home/peter/src/racer.go:8 +0x42
==================
Found 1 data race(s)
exit status 66
$
abc
xyz

Go运行时标记从不同goroutine更新的可变
string
变量的不一致状态。例如,写入
字符串
变量

package main

import "time"

var s string = "abc"

func main() {
    go func() {
        for {
            s = "abc"
        }
    }()
    go func() {
        for {
            s = "abc"
        }
    }()
    time.Sleep(1 * time.Second)
}
输出:

5:7: cannot assign to s[0]
$ go run -race racer.go
==================
WARNING: DATA RACE
Write at 0x00000050d360 by goroutine 6:
  main.main.func2()
      /home/peter/src/racer.go:15 +0x3a

Previous write at 0x00000050d360 by goroutine 5:
  main.main.func1()
      /home/peter/src/racer.go:10 +0x3a

Goroutine 6 (running) created at:
  main.main()
      /home/peter/src/racer.go:13 +0x5a

Goroutine 5 (running) created at:
  main.main()
      /home/peter/src/racer.go:8 +0x42
==================
Found 1 data race(s)
exit status 66
$
abc
xyz

原职:


字符串类型表示一组字符串值。字符串值是一个 (可能为空)字节序列。字符串是不可变的:一次 创建时,无法更改字符串的内容


字符串
变量不是不可变的。它包含一个字符串描述符,一个
struct

type stringStruct struct {
    str unsafe.Pointer
    len int
}
比如说,

package main

func main() {
    var s string = "abc"
    s[0] = '0'
}
package main

import "fmt"

func main() {
    s := "abc"
    fmt.Println(s)
    s = "xyz"
    fmt.Println(s)

}
// error: cannot assign to s[0]
s[0] = '0'
输出:

5:7: cannot assign to s[0]
$ go run -race racer.go
==================
WARNING: DATA RACE
Write at 0x00000050d360 by goroutine 6:
  main.main.func2()
      /home/peter/src/racer.go:15 +0x3a

Previous write at 0x00000050d360 by goroutine 5:
  main.main.func1()
      /home/peter/src/racer.go:10 +0x3a

Goroutine 6 (running) created at:
  main.main()
      /home/peter/src/racer.go:13 +0x5a

Goroutine 5 (running) created at:
  main.main()
      /home/peter/src/racer.go:8 +0x42
==================
Found 1 data race(s)
exit status 66
$
abc
xyz
字符串
内容是不可变的。比如说,

package main

func main() {
    var s string = "abc"
    s[0] = '0'
}
package main

import "fmt"

func main() {
    s := "abc"
    fmt.Println(s)
    s = "xyz"
    fmt.Println(s)

}
// error: cannot assign to s[0]
s[0] = '0'

您需要同步才能访问字符串变量。

在语言语法级别或标准库中对字符串类型定义的所有函数都会返回一个新的字符串实例。没有一个函数在适当的位置改变字符串。只要遵循这一做法,您将同时安全

字符串是不可变的,但指针不是。如果您没有使用指向字符串的指针,那么您也不会在线程之间共享数据,所以不可变性是不相关的;如果您使用的是指针,则需要对其进行同步。