我有一张地图,由goroutine A使用,并在goroutine 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
}
}
你看到这个伪代码有什么问题吗? (就简陋而言)
答案 0 :(得分:6)
代码不安全,因为对指针值的赋值和读取不保证是原子的。这可能意味着当一个goroutine写入新指针值时,另一个可能会看到来自旧值和新值的混合字节,这将导致程序以令人讨厌的方式死亡。可能发生的另一件事是,由于代码中没有同步,编译器可能会注意到goroutineA中没有任何内容可以更改,并将tempA := a
语句从循环中取出。这意味着你永远不会看到新的地图分配,因为另一个goroutine会更新它们。
您可以使用go test -race
自动查找这些问题。
一种解决方案是使用互斥锁锁定对地图的所有访问权限。
您可能希望阅读Go Memory Model文档,该文档清楚地解释了在goroutines中可见变量的变化。
答案 1 :(得分:3)
如果对数据竞赛不确定,请运行go run -race file.go
,说的是,是的,会有比赛。
最简单的解决方法是使用sync.RWMutex:
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()
}
}