【发布时间】:2019-07-24 00:54:42
【问题描述】:
我有一个结构,其中包含一个可以从受互斥锁保护的不同 goroutine 访问的映射:
type Readings struct {
sync.Mutex
Timestamp time.Time
values map[meters.Measurement]float64
}
不过,它在Add(写入)和MarshalJSON(读取)之间存在数据竞争。后者进行结构转换:
func (r *Readings) Add(q QuerySnip) {
r.Lock()
defer r.Unlock()
r.Timestamp = q.Timestamp
if r.values == nil {
r.values = make(map[meters.Measurement]float64)
}
r.values[q.Measurement] = q.Value
}
func (r *Readings) MarshalJSON() ([]byte, error) {
r.Lock()
defer r.Unlock()
res := map[string]interface{}{
"Timestamp": r.Timestamp,
"Unix": r.Timestamp.Unix(),
}
if r.values == nil {
return json.Marshal(res)
}
for m, v := range r.values {
res[m.String()] = v
}
return json.Marshal(res)
}
这是比赛:
WARNING: DATA RACE
Write at 0x00c000294900 by goroutine 11:
runtime.mapassign_fast64()
/usr/local/opt/go/libexec/src/runtime/map_fast64.go:92 +0x0
github.com/volkszaehler/mbmd/server.(*Readings).Add()
...
Previous read at 0x00c000294900 by goroutine 25:
runtime.mapiterinit()
/usr/local/opt/go/libexec/src/runtime/map.go:804 +0x0
github.com/volkszaehler/mbmd/server.(*Readings).MarshalJSON()
/Users/andig/htdocs/mbmd/server/datagram.go:126 +0x304
github.com/volkszaehler/mbmd/server.(*data).MarshalJSON()
...
我不明白racey 在这里会发生什么,并且我无法复制将代码移动到独立测试用例中的竞争行为。
有进一步诊断的建议吗?
【问题讨论】:
-
你确定不是每个 goroutine 都有一个指向结构的单独副本的指针吗?
-
是否有可能两个
Readings共享同一个values地图? -
都对。我复制了一个读数,在副本上调用 Add,在原件上调用 MarshalJson。这些值是共享的,本身没有被深度复制?
-
@andig:地图是参考值,它们不会被自动复制 (stackoverflow.com/questions/23057785/how-to-copy-a-map)
-
如果您还复制了sync.Mutex,请注意第一次使用后无法复制互斥锁。
go vet命令标记此错误。
标签: go