运行时未检测到切片上的竞争
runtime does not detect the race on the slice
使用 go run.
执行以下程序会产生致命错误,例如
fatal error: concurrent map writes
goroutine 103 [running]:
...
运行时检测到 map
上的不正当访问,但未检测到 slice
为什么会这样?
这是我写的程序:
package main
import (
"time"
)
func main() {
m := map[string]int{"a":1}
s := []int{1}
for i := 0; i < 1000; i++ {
go func() {
m["a"] = i
s[0] = i
}()
}
time.Sleep(3 * time.Second)
}
首先,假设我们讨论的是修改切片的 内容 (不是切片定义本身),切片 是 安全的对于并发 read/write 只要您不同时并发修改相同的确切元素 。这意味着要准确检测不正确的并发访问,您需要在 每个元素 的基础上对其进行跟踪。这将是一个过多的内存开销。
其次,切片旨在作为对数组的精简抽象,数组本身是对直接内存访问(在受限范围内)的精简抽象。也就是说,预计操作会非常快。在语言级别检测不正确的并发访问会造成过多的处理开销。
在映射的情况下,首先使用数据结构的内存和处理开销足以使 运行 这种并发检查的成本相形见绌。因此,内置此方便的安全功能被视为净积极因素。
您确实可以使用 Go 的竞争检测器检测切片上的数据竞争,但正如 documentation 所指出的那样:
The cost of race detection varies by program, but for a typical program, memory usage may increase by 5-10x and execution time by 2-20x.
地图中的内置种族检测器是唾手可得的成果。该功能使用备用 bit in the map header for storage. The feature has a very low CPU cost. Search for hashWriting
in runtime/map.go 查看实现。
切片的内置竞争检测器会产生内存开销、CPU 开销或两者兼而有之,因为每个切片元素在 memory model 中都是一个单独的变量。每个切片元素没有备用位可用于竞争检测器实现。
使用 Go Race Detector 检测所有数据竞争,包括切片元素上的竞争。 Go Race Detector 检测代码以记录和检测内存的访问方式。
即使未启用竞争检测器,运行时也会在发生不正当访问时警告最终用户。
如果像 go run -race .
那样在启用竞争检测器的情况下执行该程序,您会得到这样的输出
$ go run -race .
...
==================
WARNING: DATA RACE
Write at 0x00c000124000 by goroutine 8:
main.main.func1()
/home/mh-cbon/gow/src/test/d/dr/main.go:14 +0xd7
Previous write at 0x00c000124000 by goroutine 6:
main.main.func1()
/home/mh-cbon/gow/src/test/d/dr/main.go:14 +0xd7
Goroutine 8 (running) created at:
main.main()
/home/mh-cbon/gow/src/test/d/dr/main.go:12 +0x12a
Goroutine 6 (finished) created at:
main.main()
/home/mh-cbon/gow/src/test/d/dr/main.go:12 +0x12a
==================
Found 4 data race(s)
exit status 66
这意味着在第 14 行 (s[0] = i
) 并发访问从第 12 行 go func() {
.
开始
阅读有关竞争检测器的更多信息
使用 go run.
执行以下程序会产生致命错误,例如
fatal error: concurrent map writes
goroutine 103 [running]:
...
运行时检测到 map
上的不正当访问,但未检测到 slice
为什么会这样?
这是我写的程序:
package main
import (
"time"
)
func main() {
m := map[string]int{"a":1}
s := []int{1}
for i := 0; i < 1000; i++ {
go func() {
m["a"] = i
s[0] = i
}()
}
time.Sleep(3 * time.Second)
}
首先,假设我们讨论的是修改切片的 内容 (不是切片定义本身),切片 是 安全的对于并发 read/write 只要您不同时并发修改相同的确切元素 。这意味着要准确检测不正确的并发访问,您需要在 每个元素 的基础上对其进行跟踪。这将是一个过多的内存开销。
其次,切片旨在作为对数组的精简抽象,数组本身是对直接内存访问(在受限范围内)的精简抽象。也就是说,预计操作会非常快。在语言级别检测不正确的并发访问会造成过多的处理开销。
在映射的情况下,首先使用数据结构的内存和处理开销足以使 运行 这种并发检查的成本相形见绌。因此,内置此方便的安全功能被视为净积极因素。
您确实可以使用 Go 的竞争检测器检测切片上的数据竞争,但正如 documentation 所指出的那样:
The cost of race detection varies by program, but for a typical program, memory usage may increase by 5-10x and execution time by 2-20x.
地图中的内置种族检测器是唾手可得的成果。该功能使用备用 bit in the map header for storage. The feature has a very low CPU cost. Search for hashWriting
in runtime/map.go 查看实现。
切片的内置竞争检测器会产生内存开销、CPU 开销或两者兼而有之,因为每个切片元素在 memory model 中都是一个单独的变量。每个切片元素没有备用位可用于竞争检测器实现。
使用 Go Race Detector 检测所有数据竞争,包括切片元素上的竞争。 Go Race Detector 检测代码以记录和检测内存的访问方式。
即使未启用竞争检测器,运行时也会在发生不正当访问时警告最终用户。
如果像 go run -race .
那样在启用竞争检测器的情况下执行该程序,您会得到这样的输出
$ go run -race .
...
==================
WARNING: DATA RACE
Write at 0x00c000124000 by goroutine 8:
main.main.func1()
/home/mh-cbon/gow/src/test/d/dr/main.go:14 +0xd7
Previous write at 0x00c000124000 by goroutine 6:
main.main.func1()
/home/mh-cbon/gow/src/test/d/dr/main.go:14 +0xd7
Goroutine 8 (running) created at:
main.main()
/home/mh-cbon/gow/src/test/d/dr/main.go:12 +0x12a
Goroutine 6 (finished) created at:
main.main()
/home/mh-cbon/gow/src/test/d/dr/main.go:12 +0x12a
==================
Found 4 data race(s)
exit status 66
这意味着在第 14 行 (s[0] = i
) 并发访问从第 12 行 go func() {
.