今天出现了一个有趣的问题,我的代码包含多个Mutex,每个Mutex都包含对不同地图的锁定。
我正在使用的源代码类似于struct :
type MyStruct struct {
dogMutex sync.RWMutex
dogMap map[int]Dog // keyed by PID
catMutex sync.RWMutex
catMap map[int]Cat // keyed by (localAddress + localPort)
}
此问题的更详细示例如下: https://play.golang.org/p/eic8q2VrNq
用'构建可执行文件后构建-race ..."生成的可执行文件报告以下竞争
由于代码比上面的示例更复杂,有趣的是注意到数据竞争是在代码中指示的区域上报告的。
以下堆栈来自实际应用程序。
1)在wwww.go:95上报告的写入操作等同于我在代码中的WRITE注释(MethodOne)
2)在wwww.go:218上报告的先前阅读操作等同于我在代码中的READ注释(MethodTwo)
=================
WARNING: DATA RACE
Write at 0x00c420017890 by goroutine 97:
runtime.mapassign1()
/usr/local/go/src/runtime/hashmap.go:442 +0x0
main.(*NetworkManager).MethodOne()
/opt/doppler/src/xxx/yyy/wwww.go:95 +0x745
Previous read at 0x00c420017890 by goroutine 70:
runtime.mapiterinit()
/usr/local/go/src/runtime/hashmap.go:620 +0x0
main.NetworkManager.MethodTwo()
/opt/xxx/src/xxx/yyy/wwww.go:218 +0x1e9
main.(*NetworkManager).SomethingELse()
/opt/xxx/src/xxx/yyy/wwww.go:174 +0x99d
main.(*NetworkManager).SomethingFurther()
/opt/xxx/src/xxx/yyy/wwww.go:102 +0x3c
我想知道这是否是使用互斥锁的正确方法。我的代码有很多并发性,但我把这个问题集中在竞赛检测器根据苹果与香蕉(两个完全不同的互斥体)进行报告的事实
答案 0 :(得分:1)
只是想猜一下,但是这个问题的一个常见原因是意外地通过值而不是引用传递包含指针的结构。例如:
type MyStruct struct {
dogMutex sync.RWMutex
dogMap map[int]Dog // keyed by PID
catMutex sync.RWMutex
catMap map[int]Cat // keyed by (localAddress + localPort)
}
func (s MyStruct) Example() {
// this lock doesn't actually work, but the map access does because its a
// reference type
s.catMutex.Lock()
s.catMap[1] = Cat{}
s.catMutex.Unlock()
}
另一种可能性是,您可能正在将地图传递到您的init:
func (s *MyStruct) Init(cats map[int]Cat) {
s.catMap = cats
}
然后你在结构之外的某个地方修改地图。如果是这种情况,则需要创建新地图并复制所有值。
顺便说一下,go vet
可以检测到其中的许多问题,请尝试在代码上运行它:https://golang.org/cmd/vet/。 (另外值得一试的是metalinter)