go - 为什么报告此数据竞赛?

标签 go race-condition

我使用“-race”标志构建了以下代码并运行了它(版本go1.14.1 linux/amd64),并报告了一些数据争用情况(请参见下文)。有时只报告一次数据竞赛,有时则报告三场。第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中创建的映射(传递给函数update以便在第40行进行修改)与在第24行读取的映射不同。指针替换发生在更新完成之后,为什么会有这样的数据种族:
==================
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号线,比赛就结束了。

最佳答案

当第二个goroutine在不锁定的情况下从handle()读取时,pm正在写入pm(指针pm,而不是 map 内容)。这就是报告种族的原因。使用锁访问pm时,goroutine无法在写入过程中读取pm

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

种族探测器检测到对 map 的锁定/解锁读/写访问。读取goroutine正在不加锁的情况下从映射中进行读取,而写入goroutine正在不加锁的状态下写入相同的映射。这就是种族探测器所提示的。

关于go - 为什么报告此数据竞赛?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61429869/

相关文章:

go - 传递任意结构作为函数参数

java - HashTable 同步方法和竞争条件

javascript - 在我 "reset"之后 setTimeout 会触发两次吗?

ruby-on-rails - 在 RSpec 单元测试中模拟竞争条件

mongodb - 在GraphQL应用程序中实现 Swagger 最佳方法

logging - Seelog rollingfile 文件名模式不符合预期

go - 为 io.Reader 添加前缀

javascript - 使用 Javascript 和不一致的数组值洗牌?

objective-c - 发生读取时,Objective C 锁定写入操作

concurrency - 即使 channel 关闭,goroutines 也会死锁