两个不同的互斥锁报告了数据竞争。

huangapple go评论89阅读模式
英文:

data race reported from two distinct mutexes

问题

今天出现了一个有趣的问题,我有一段代码中包含了多个互斥锁,每个锁都用于不同的映射。

这是我正在使用的源代码的类似结构:

type MyStruct struct {
    dogMutex sync.RWMutex
    dogMap   map[int]Dog // 以PID为键
    catMutex sync.RWMutex
    catMap   map[int]Cat // 以(localAddress + localPort)为键
}

这里有一个更详细的问题示例:链接

在使用"go build -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

我想知道这是否是使用互斥锁的正确方式。我的代码具有很高的并发性,但我将此问题集中在竞争检测器基于Apples vs Bananas(两个完全不同的互斥锁)的报告上。

英文:

An interesting issue has arisen today where I have code that contains more than one Mutex, each covering locking for distinct Maps.

Here is a similar in struct of my source code that I am using:

type MyStruct struct {
	dogMutex sync.RWMutex
	dogMap   map[int]Dog // keyed by PID
	catMutex sync.RWMutex
	catMap   map[int]Cat // keyed by (localAddress + localPort)
}

A more detailed example of the issue is here:
https://play.golang.org/p/eic8q2VrNq

After building the executable with 'go build -race ..." the generated executable reports the following race

As the code is way more complex than the example above, it is interesting to notice that the data race is reported on the areas indicated in the code.

The following stack is from the real application.

  1. The write operation reported on wwww.go:95 is equivalent to my WRITE comment in the code (MethodOne)

  2. The previous read operattion reported on wwww.go:218 is equivalent to my READ comment in the code (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

I am wondering if this is a proper way of using mutexes. My code has much concurrency, but I am focusing this question on the fact that the race detector is reporting based on Apples vs Bananas (two completely distinct mutexes)

答案1

得分: 1

只是猜测一下,但这个问题的一个常见原因是意外地按值而不是按引用传递包含指针的结构体。例如:

type MyStruct struct {
    dogMutex sync.RWMutex
    dogMap   map[int]Dog // 以PID为键
    catMutex sync.RWMutex
    catMap   map[int]Cat // 以(localAddress + localPort)为键
}

func (s MyStruct) Example() {
    // 这个锁实际上不起作用,但是map的访问是有效的,因为它是引用类型
    s.catMutex.Lock()
    s.catMap[1] = Cat{}
    s.catMutex.Unlock()
}

另一个可能性是,也许你将map传递到了你的init函数中:

func (s *MyStruct) Init(cats map[int]Cat) {
    s.catMap = cats
}

然后你在结构体之外的某个地方修改了这个map。如果是这种情况,你需要创建一个新的map并复制所有的值。

顺便说一句,go vet可以检测出许多这些问题,请尝试在你的代码上运行它:https://golang.org/cmd/vet/。(还值得尝试的是 metalinter

英文:

Just going to take a guess, but one common cause of this issue is accidentally passing the struct which contains the pointers by value instead of by reference. For example:

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()
}

Another possibility is that perhaps you are passing the maps into your init:

func (s *MyStruct) Init(cats map[int]Cat) {
    s.catMap = cats
}

And then you are modifying the map somewhere outside of the struct. If that's the case you need to create a new map and copy all the values.

Incidentally go vet can detect many of these issues, try running it on your code: https://golang.org/cmd/vet/. (Also worth a try is the metalinter)

huangapple
  • 本文由 发表于 2016年11月26日 00:07:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/40808869.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定