【发布时间】:2020-05-22 14:46:05
【问题描述】:
问题涉及以下代码:
package main
import "fmt"
func main() {
var counters = map[int]int{}
for i := 0; i < 5; i++ {
go func(counters map[int]int, th int) {
for j := 0; j < 5; j++ {
counters[th*10+j]++
}
}(counters, i)
}
fmt.Scanln()
fmt.Println("counters result", counters)
}
这是我使用go run -race race.go 运行此代码时得到的输出
$ go run -race race.go
==================
WARNING: DATA RACE
Read at 0x00c000092150 by goroutine 8:
runtime.mapaccess1_fast64()
/usr/lib/go-1.13/src/runtime/map_fast64.go:12 +0x0
main.main.func1()
/tmp/race.go:10 +0x6b
Previous write at 0x00c000092150 by goroutine 7:
runtime.mapassign_fast64()
/usr/lib/go-1.13/src/runtime/map_fast64.go:92 +0x0
main.main.func1()
/tmp/race.go:10 +0xaf
Goroutine 8 (running) created at:
main.main()
/tmp/race.go:8 +0x67
Goroutine 7 (finished) created at:
main.main()
/tmp/race.go:8 +0x67
==================
==================
WARNING: DATA RACE
Read at 0x00c0000aa188 by main goroutine:
reflect.typedmemmove()
/usr/lib/go-1.13/src/runtime/mbarrier.go:177 +0x0
reflect.copyVal()
/usr/lib/go-1.13/src/reflect/value.go:1297 +0x7b
reflect.(*MapIter).Value()
/usr/lib/go-1.13/src/reflect/value.go:1251 +0x15e
internal/fmtsort.Sort()
/usr/lib/go-1.13/src/internal/fmtsort/sort.go:61 +0x259
fmt.(*pp).printValue()
/usr/lib/go-1.13/src/fmt/print.go:773 +0x146f
fmt.(*pp).printArg()
/usr/lib/go-1.13/src/fmt/print.go:716 +0x2ee
fmt.(*pp).doPrintln()
/usr/lib/go-1.13/src/fmt/print.go:1173 +0xad
fmt.Fprintln()
/usr/lib/go-1.13/src/fmt/print.go:264 +0x65
main.main()
/usr/lib/go-1.13/src/fmt/print.go:274 +0x13c
Previous write at 0x00c0000aa188 by goroutine 10:
main.main.func1()
/tmp/race.go:10 +0xc4
Goroutine 10 (finished) created at:
main.main()
/tmp/race.go:8 +0x67
==================
counters result map[0:1 1:1 2:1 3:1 4:1 10:1 11:1 12:1 13:1 14:1 20:1 21:1 22:1 23:1 24:1 30:1 31:1 32:1 33:1 34:1 40:1 41:1 42:1 43:1 44:1]
Found 2 data race(s)
exit status 66
这是我无法理解的。为什么会有比赛条件?我们不是只有一个 goroutine 可以读取/写入值吗?例如例程 0 将仅修改 counter[0] 到 counters[4] 中的值,例程 1 将仅修改 counters[10] 到 counters[14] 中的值,例程 2 将仅修改 counters[20] 到 counters[24] 中的值等等。我在这里没有看到比赛条件。感觉好像我错过了什么。有人能对此有所了解吗?
仅供参考,我是新手。如果您能简化解释(如果可能的话),我将不胜感激。
【问题讨论】:
-
sync.Map(而不是常规映射和互斥锁)的一个特定用例是“多个 goroutine 读取、写入和覆盖不相交键集的条目”时,因此它可能在这种情况下值得研究。 -
同意@hobbs,请记住
sync.Map是“无类型的”(它以interface{}值运行)并且它的性能可能比受保护的地图更差通过特定条件下的互斥锁 - 那些与sync.Map不同的情况进行了优化。因此,编程时的经验法则几乎总是首先实现最易于理解的代码,然后才尝试消除性能瓶颈,如果有的话。
标签: go race-condition