运行时无法检测到切片上的竞争。

huangapple go评论82阅读模式
英文:

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)
}
英文:

Executing below program with go run. yields fatal errors such as

fatal error: concurrent map writes

goroutine 103 [running]:
...

The runtime detects the racy access on the map, but not on the slice

Why it is so ?

This is the program i have written:

package main

import (
	&quot;time&quot;
)

func main() {
	m := map[string]int{&quot;a&quot;:1}
	s := []int{1}

	for i := 0; i &lt; 1000; i++ {
		go func() {
			m[&quot;a&quot;] = i
			s[0] = i
		}()
	}

	time.Sleep(3 * time.Second)
}

答案1

得分: 2

内置的映射(map)中的竞争检测器是低成本的。该功能使用映射头部中的一个备用位来存储信息。该功能的CPU成本非常低。在runtime/map.go中搜索hashWriting以查看其实现。

对于切片(slice)来说,内置的竞争检测器将产生内存开销、CPU开销或两者同时存在的情况,因为每个切片元素在内存模型中都是一个单独的变量。在竞争检测器的实现中,没有每个切片元素可用的备用位。

使用Go竞争检测器来检测所有数据竞争,包括对切片元素的竞争。Go竞争检测器会对代码进行插装,记录和检测内存访问方式。

英文:

The builtin race detector in maps is low-hanging fruit. The feature uses a spare bit in the map header for storage. The feature has a very low CPU cost. Search for hashWriting in runtime/map.go to see the implementation.

A builtin in race detector for slices will incur memory overhead, CPU overhead or both because each slice element is a separate variable in the memory model. There's not a spare bit per slice element to use in a race detector implementation.

Use the Go Race Detector to detect all data races including races on slice elements. The Go Race Detector instruments the code to record and detect how memory is accessed.

答案2

得分: 1

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

如果你使用启用了竞争检测器的命令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上了解更多关于竞争检测器的信息。

英文:

the runtime is kind enough to warn the end user when a racy access happens even though the race detector was not enabled.

If you execute that program with the race detector enabled like in go run -race . you get such output

$ 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

Which means that at line 14 (s[0] = i) there is concurrent access started at line 12 go func() {.

Read more about the race detector at https://go.dev/blog/race-detector

答案3

得分: 0

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

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

在映射的情况下,使用该数据结构的内存和处理开销足以使运行此并发检查的成本相形见绌。因此,将此便捷的安全功能内置被视为净正面。

你确实可以使用Go的竞争检测器检测切片上的数据竞争,但正如文档所指出的:
> 竞争检测的成本因程序而异,但对于典型的程序,内存使用可能增加5-10倍,执行时间增加2-20倍。

英文:

Firstly, assuming we're talking about modifying the contents of the slice (not the slice definition itself), slices are safe for concurrent read/write so long as you don't concurrently modify the same exact element at the same time. This means that to accurately detect incorrect concurrent access, you would need to track it on a per-element basis. This would be an excessive memory overhead.

Secondly, slices are intended as a thin abstraction over arrays, which themselves are a thin abstraction over direct memory access (within a constrained range). That is to say, the operation is expected to be very fast. Detecting incorrect concurrent access at the language level would be an excessive processing overhead.

In the case of maps, both the memory and processing overhead of using the data structure in the first place is enough to dwarf the cost of running this concurrency check. Therefore, it is seen as a net-positive to have this handy safety feature built in.

You can indeed detect data races on slices using Go's race detector, but as noted by the 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.

huangapple
  • 本文由 发表于 2021年10月10日 11:07:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/69511989.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定