为什么会报道这种数据竞赛



我用"-race"标志构建了以下代码并运行了它(go版本go1.14.1linux/aamd64(,报告了一些数据争用(见下文(。有时只报告了一次数据竞赛,有时报告了三次。24号线和35号线之间的数据竞赛是可以理解的,但我不明白为什么24号线与40号线之间会出现数据竞赛。

1 package main
2 
3 import (
4         "sync"
5 )
6 
7 var (
8         m          = make(map[string]string)
9         pm         = &m
10         updateLock = sync.RWMutex{}
11 )
12 
13 func main() {
14         wg := &sync.WaitGroup{}
15         wg.Add(2)
16         go func() {
17                 defer wg.Done()
18                 handle()
19         }()
20 
21         go func() {
22                 defer wg.Done()
23                 //updateLock.RLock()
24                 if _, ok := (*pm)["test"]; ok {
25                 }
26                 //updateLock.RUnlock()
27         }()
28         wg.Wait()
29 }
30 
31 func handle() {
32         newMap := make(map[string]string)
33         update(&newMap)
34         updateLock.Lock()
35         pm = &newMap
36         updateLock.Unlock()
37 }
38 
39 func update(ptrMap *map[string]string) {
40         (*ptrMap)["test"] = "test"
41 }

我认为在函数handle中创建的映射与在第24行读取的映射不同,该映射在第40行被传递给函数update以进行修改。指针替换发生在更新完成之后,那么为什么会出现这样的数据竞赛:

==================
WARNING: DATA RACE
Read at 0x00c000070030 by goroutine 7:
runtime.mapaccess2_faststr()
/home/vagrant/.go/src/runtime/map_faststr.go:107 +0x0
main.main.func2()
/vagrant/go_projects/src/learn/race/main.go:24 +0xd1
Previous write at 0x00c000070030 by goroutine 6:
runtime.mapassign_faststr()
/home/vagrant/.go/src/runtime/map_faststr.go:202 +0x0
main.update()
/vagrant/go_projects/src/learn/race/main.go:40 +0xba
main.handle()
/vagrant/go_projects/src/learn/race/main.go:33 +0x7f
main.main.func1()
/vagrant/go_projects/src/learn/race/main.go:18 +0x5f
Goroutine 7 (running) created at:
main.main()
/vagrant/go_projects/src/learn/race/main.go:21 +0xc4
Goroutine 6 (finished) created at:
main.main()
/vagrant/go_projects/src/learn/race/main.go:16 +0xa2
==================

附言:当第23行和第26行被取消注释时,比赛就结束了。

handle()正在写入pm(指针pm,而不是映射内容(,而第二个goroutine正在从pm读取而没有锁定。这就是报告竞争的原因。使用锁访问pm时,goroutine在写入期间无法读取pm

当goroutine从pm读取时,有可能pm尚未初始化。

竞赛检测器检测对地图的锁定/解锁读/写访问。读取goroutine是从没有锁的映射中读取,而写入goroutine则是在没有锁的情况下写入同一个映射。这就是种族检测器所抱怨的。

最新更新