我有一个映射,由程序a使用,并在程序b中替换一次。这里的替换是指:
var a map[T]N
// uses the map
func goroutineA() {
for (...) {
tempA = a
..uses tempA in some way...
}
}
//refreshes the map
func gorountineB() {
for (...) {
time.Sleep(10 * time.Seconds)
otherTempA = make(map[T]N)
...initializes other tempA....
a = otherTempA
}
}
你觉得这段伪代码有什么问题吗?(在并发性方面)
代码不安全,因为对指针值的赋值和读取不能保证是原子性的。这可能意味着,当一个程序写入新的指针值时,另一个程序可能会看到来自旧值和新值的混合字节,这将导致程序以一种糟糕的方式死亡。另一件可能发生的事情是,由于代码中没有同步,编译器可能会注意到没有任何东西可以改变gooutinea中的a,并将tempA := a
语句从循环中取出。这意味着你永远不会看到新的映射赋值,因为其他例程更新了它们。
您可以使用go test -race
自动查找这些类型的问题。
一个解决方案是用互斥锁锁定所有对map的访问。
你可能希望阅读Go内存模型文档,它清楚地解释了变量的更改何时在程序中可见。
当不确定数据竞赛时,运行go run -race file.go
,也就是说,会有一场竞赛。
var a map[T]N
var lk sync.RWMutex
// uses the map
func goroutineA() {
for (...) {
lk.RLock()
//actions on a
lk.RUnlock()
}
}
//refreshes the map
func gorountineB() {
for (...) {
otherTempA = make(map[T]N)
//...initializes other tempA....
lk.Lock()
a = otherTempA
lk.Unlock()
}
}