Go 的 string 是否线程安全
# Go 的 string 是否线程安全
string
是 Go 的内建类型,我们知道string
的值是不可变的,但string
变量不是。如果多个 goroutine 同时修改同一个string
变量,需要添加线程安全机制,例如锁或者原子操作。
# string
的值是不可变的
func TestModifyString(t *testing.T) {
var s string = "abc"
s[0] = '0' // Cannot assign to s[0]
}
1
2
3
4
2
3
4
执行这个测试得到的结果:
cannot assign to s[0] (value of type byte)
1
# string
变量是可变的
func TestString2(t *testing.T) {
a := "hello"
a = "world"
fmt.Println(a)
}
1
2
3
4
5
2
3
4
5
执行这个测试得到的结果:
world
1
# string
线程不安全
string
底层是一个struct
类型,包含一个字符串描述符str
和一个长度len
。
runtime/string.go
type stringStruct struct {
str unsafe.Pointer
len int
}
1
2
3
4
2
3
4
下面这个测试可以检测string
是否线程安全:
func TestString(t *testing.T) {
ch := make(chan string)
a := "1"
go func() {
i := 0
for {
if i%2 == 0 {
a = "1"
} else {
a = "22"
}
time.Sleep(time.Millisecond * 1) // 阻止编译器优化
i++
}
}()
go func() {
for {
b := a
if b != "1" && b != "22" {
ch <- b
}
}
}()
for i := 0; i < 10; i++ {
fmt.Println("Got string: ", <-ch)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
执行这个测试得到的结果:
Got string: 2
Got string: 2
Got string: 15
Got string: 2
Got string: 2
Got string: 15
Got string: 15
Got string: 15
Got string: 2
Got string: 2
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
可以看到在频繁的写入操作中,另一协程可能读到部分写入的结果跟预想的不一致。(len
为1,指针指向22
,或者是len
为2,指针指向了1
)。
在并发场景下,string
跟interface
一样,都是需要使用atomic
包来保证读写的原子性。
# 参考
https://stackoverflow.com/questions/51249918/immutability-of-string-and-concurrency
评论
上次更新: 2024/05/29, 14:25:22