【问题标题】:runtime does not detect the race on the slice运行时未检测到切片上的竞争
【发布时间】:2021-11-29 08:45:52
【问题描述】:

使用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)
}

【问题讨论】:

    标签: dictionary go concurrency


    【解决方案1】:

    地图中的内置种族检测器是唾手可得的果实。该功能使用map header 中的备用bit 进行存储。该功能的 CPU 成本非常低。在runtime/map.go 中搜索hashWriting 以查看实现。

    切片的内置竞争检测器将产生内存开销、CPU 开销或两者兼而有之,因为每个切片元素都是memory model 中的一个单独变量。在竞争检测器实现中,每个切片元素都没有备用位。

    使用Go Race Detector 检测所有数据竞争,包括切片元素上的竞争。 Go Race Detector 检测代码以记录和检测内存的访问方式。

    【讨论】:

    • 谢谢,非常有帮助。还有一个问题,如果这个bit属于hmap但是map中的每个元素,那么如果多个goroutine并发写入一个具有完全不同key的map,它仍然会导致concurrent map writes错误,这合理吗?
    • 当多个 goroutine 写入一个映射中的不同键时,内置竞争检测器会报告竞争。这是一场竞赛,因为具有所有键和值的映射是内存模型中的单个变量。
    • 非常感谢,非常有帮助。
    【解决方案2】:

    首先,假设我们正在讨论修改切片的内容(不是切片定义本身),切片对于并发读/写是安全的只要您不同时修改相同的确切元素。这意味着要准确检测不正确的并发访问,您需要在每个元素 的基础上对其进行跟踪。这将是过多的内存开销。

    其次,切片旨在作为对数组的精简抽象,数组本身是对直接内存访问(在受限范围内)的精简抽象。也就是说,操作预计会非常快。在语言级别检测不正确的并发访问将是一个过多的处理开销。

    在地图的情况下,首先使用数据结构的内存和处理开销都足以使运行此并发检查的成本相形见绌。因此,内置这种方便的安全功能被视为一个净积极因素。

    您确实可以使用 Go 的竞争检测器检测切片上的数据竞争,但正如 documentation 所述:

    竞态检测的成本因程序而异,但对于典型程序,内存使用量可能会增加 5-10 倍,执行时间可能会增加 2-20 倍。

    【讨论】:

      【解决方案3】:

      即使未启用竞争检测器,运行时也会在发生恶意访问时警告最终用户。

      如果你像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了解更多关于比赛检测器的信息

      【讨论】:

        猜你喜欢
        • 2017-02-20
        • 1970-01-01
        • 2021-08-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-26
        相关资源
        最近更新 更多