运行时未检测到切片上的竞争

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() {.

开始

https://go.dev/blog/race-detector

阅读有关竞争检测器的更多信息