Почему сообщается об этой гонке данных? - PullRequest
0 голосов
/ 25 апреля 2020

Я собрал следующий код с флагом «-race» и запустил его (go версия 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
==================

PS Когда строки 23 и 26 не прокомментированы, гонка исчезает.

1 Ответ

2 голосов
/ 25 апреля 2020

handle() записывает в pm (указатель pm, а не содержимое карты), в то время как вторая программа считывает из pm без блокировки. Именно поэтому сообщается о расе. Когда вы получаете доступ к pm с помощью блокировки, программа не может прочитать pm во время записи.

Существует вероятность того, что, когда программа читает с pm, pm еще не инициализирована.

Детектор гонки обнаруживает заблокированный / разблокированный доступ для чтения / записи к карте. Программа чтения читает с карты без блокировки, а программа записи пишет на той же карте без блокировки. Вот на что жалуется детектор расы.

...