我是并发新手,面临以下问题。 我有一个外部 for 循环,用于从数据库获取行。我希望在每个循环中填充顶点分布图
for year := estart; year <= eend; year++ {
vertexDistribution[year] += degree
}
但是当我从数据库获取的 id 与之前的 id 不同时,我希望 go 例程填充 DegreeDistribution 映射(从 vertexDistribution 映射获取其数据)。
timeStart := time.Now()
for rows.Next() {
err = rows.Scan(&sourceid, °ree, &estart, &eend)
if err != nil {
log.Fatal("Error scanning the rows of degree distribution: ", err)
}
if prevsourceid != sourceid && prevsourceid != "" {
mutex.Lock()
temp := vertexDistribution
mutex.Unlock()
vertexDistribution = make(map[int]int) // clear the map
go func(temp map[int]int) {
mutex.Lock()
defer mutex.Unlock()
for k, v := range temp {
if _, ok := degreeDistribution[k]; !ok {
degreeDistribution[k] = make(map[int]int)
}
degreeDistribution[k][v]++
}
}(temp)
}
for year := estart; year <= eend; year++ {
vertexDistribution[year] += degree
}
prevsourceid = sourceid
}
mutex.Lock()
defer mutex.Unlock()
for k, v := range vertexDistribution { // covering the last vertexDistribution data
if _, ok := degreeDistribution[k]; !ok {
degreeDistribution[k] = make(map[int]int)
}
degreeDistribution[k][v]++
}
elapsedTime := time.Since(timeStart)
fmt.Println(elapsedTime.Seconds(), "seconds elapsed getting the degree distribution")
显然我的概念是错误的,有时输出是正确的,有时是错误的,有时我得到致命错误:并发地图迭代和地图写入
我跑了 go run -race main.go 并得到了
==================
WARNING: DATA RACE
Read at 0x00c000192000 by main goroutine:
reflect.maplen()
/usr/local/go/src/runtime/map.go:1406 +0x0
reflect.Value.lenNonSlice()
/usr/local/go/src/reflect/value.go:1785 +0x1e9
reflect.Value.Len()
/usr/local/go/src/reflect/value.go:1774 +0x137
internal/fmtsort.Sort()
/usr/local/go/src/internal/fmtsort/sort.go:58 +0x121
fmt.(*pp).printValue()
/usr/local/go/src/fmt/print.go:816 +0x1144
fmt.(*pp).printValue()
/usr/local/go/src/fmt/print.go:827 +0x2c8f
fmt.(*pp).printArg()
/usr/local/go/src/fmt/print.go:759 +0xb84
fmt.(*pp).doPrintln()
/usr/local/go/src/fmt/print.go:1221 +0x4b
fmt.Fprintln()
/usr/local/go/src/fmt/print.go:304 +0x52
fmt.Println()
/usr/local/go/src/fmt/print.go:314 +0x247
main.main()
/home/xaris/VScode/HiNode-CockroachDB/main.go:21 +0x1e6
Previous write at 0x00c000192000 by goroutine 22575:
runtime.mapassign_fast64()
/usr/local/go/src/runtime/map_fast64.go:93 +0x0
hinode/models.MultiTable.GetDegreeDistributionConcurrently.func1()
/home/xaris/VScode/HiNode-CockroachDB/models/multiTable.go:352 +0x184
hinode/models.MultiTable.GetDegreeDistributionConcurrently.gowrap1()
/home/xaris/VScode/HiNode-CockroachDB/models/multiTable.go:354 +0x41
Goroutine 22575 (finished) created at:
hinode/models.MultiTable.GetDegreeDistributionConcurrently()
/home/xaris/VScode/HiNode-CockroachDB/models/multiTable.go:345 +0x8b6
main.main()
/home/xaris/VScode/HiNode-CockroachDB/main.go:19 +0x1d6
==================
==================
WARNING: DATA RACE
Read at 0x00c0000eaca8 by main goroutine:
reflect.typedmemmove()
/usr/local/go/src/runtime/mbarrier.go:203 +0x0
reflect.copyVal()
/usr/local/go/src/reflect/value.go:2064 +0x5b
reflect.(*MapIter).Value()
/usr/local/go/src/reflect/value.go:1961 +0x124
internal/fmtsort.Sort()
/usr/local/go/src/internal/fmtsort/sort.go:64 +0x3b6
fmt.(*pp).printValue()
/usr/local/go/src/fmt/print.go:816 +0x1144
fmt.(*pp).printValue()
/usr/local/go/src/fmt/print.go:827 +0x2c8f
fmt.(*pp).printArg()
/usr/local/go/src/fmt/print.go:759 +0xb84
fmt.(*pp).doPrintln()
/usr/local/go/src/fmt/print.go:1221 +0x4b
fmt.Fprintln()
/usr/local/go/src/fmt/print.go:304 +0x52
fmt.Println()
/usr/local/go/src/fmt/print.go:314 +0x247
main.main()
/home/xaris/VScode/HiNode-CockroachDB/main.go:21 +0x1e6
Previous write at 0x00c0000eaca8 by goroutine 22577:
hinode/models.MultiTable.GetDegreeDistributionConcurrently.func1()
/home/xaris/VScode/HiNode-CockroachDB/models/multiTable.go:352 +0x193
hinode/models.MultiTable.GetDegreeDistributionConcurrently.gowrap1()
/home/xaris/VScode/HiNode-CockroachDB/models/multiTable.go:354 +0x41
Goroutine 22577 (finished) created at:
hinode/models.MultiTable.GetDegreeDistributionConcurrently()
/home/xaris/VScode/HiNode-CockroachDB/models/multiTable.go:345 +0x8b6
main.main()
/home/xaris/VScode/HiNode-CockroachDB/main.go:19 +0x1d6
==================
“并发映射迭代和映射写入”通常意味着您要迭代映射,然后尝试在该 for 循环中向同一映射添加新键。我没有看到你的代码示例中发生了这种情况,但恐慌应该给你一个行号来调查。
更一般地说,如果您要使用多个例程读取/写入相同的映射,我建议定义安全方法来与这些数据对象交互,然后仅使用这些方法。例如:
type Distribution struct {
store map[int]int
mu sync.Mutex
}
func (d *Distribution) Insert(k, v int) {
d.mu.Lock()
d.store[k] = v
d.mu.Unlock()
}
... additional methods for Distribution operations ...
这将迫使您思考哪些操作同时是安全的,哪些操作需要互斥,然后在进入业务逻辑之前实现一次。
此外,如果您只是将它们用于计数器,请查看同步包中的原子计数器。它们可能适合您用例的某些部分。