参考资料
https://cloud.tencent.com/developer/article/1810536
并发赋值安全/不安全的类型
-
并发赋值安全的类型:
字节型,布尔型、整型、浮点型、字符型、指针、函数
这些类型可由一条机器指令完成赋值 -
数组由一个或多个元素组成,大部分情况并发不安全
注意:当位宽不大于 64 位且是 2 的整数次幂(8,16,32,64),那么其并发赋值是安全的。 -
struct 或底层是 struct 的类型并发赋值大部分情况并发不安全,类型有:
复数、字符串、 数组、切片、字典、通道、接口
注:当 struct 赋值时退化为单个字段由一个机器指令完成赋值时,并发赋值又是安全的。这种情况有:
3.1 实部或虚部相同的复数的并发赋值
3.2 等长字符串的并发赋值
3.3 同长度同容量切片的并发赋值
3.4 同一种具体类型不同值并发赋给接口
例3.1: 实部或虚部相同的复数的并发赋值
// 线程不安全
var g complex64
go func() {
g = complex(1, 2)
}()
go func() {
g = complex(3, 4)
}()
// 线程安全
var g complex64
go func() {
g = complex(1, 2)
}()
go func() {
g = complex(1, 3) // 实部相同
}()
例3.2 等长字符串的并发赋值
// 线程不安全
var s string
go func() {
s = "ab"
}()
go func() {
s = "abc"
}()
// 线程安全
var s string
go func() {
s = "abc"
}()
go func() {
s = "abd" // 字符串长度相同, 线程安全
}()
完整例子
点击打开完整示例
func main() {
var s string
i := 0
for {
var wg sync.WaitGroup
var endWg sync.WaitGroup
// 协程 1
endWg.Add(2)
wg.Add(1)
go func() {
wg.Wait()
s = "ab"
endWg.Done()
}()
// 协程 2
go func() {
wg.Wait()
s = "abc"
endWg.Done()
}()
wg.Done() // 协程内同时进行赋值
endWg.Wait() // 等待两次并发赋值完成
i++
// 赋值异常判断
if s != "ab" && s != "abc" {
fmt.Printf("concurrent assignment error, i=%v s=%v", i, s)
break
}
}
}