3 votes

Golang Data Race, avec un statut de sortie de 66

J'ai le code suivant, et j'ai une course aux données. La fonction Round vérifie périodiquement l'exécution de la fonction de suppression du contenu de la carte. Comme je l'ai lu ici : Est-il sûr de supprimer les clés sélectionnées de la carte Golang dans une boucle d'intervalle ?

La suppression des données de la carte est sûre, mais j'ai une course aux données.

package main

import (
    "fmt"
    "sync"
    "time"
)

type City struct {
    ID string
}

type Map struct {
    sync.RWMutex
    Data map[string]City
}

var done = make(chan struct{})

func (m *Map) Round() {
    for {
        select {
        case <-time.After(2 * time.Second):
            for i, v := range m.Data {
                fmt.Println("-----", v)
                delete(m.Data, i)
            }
        case <-done:
            println("bye")
            break
        }
    }
}

func (m *Map) Add(id string, h City) {
    m.Lock()
    m.Data[id] = h
    m.Unlock()
}

func main() {
    m := Map{}
    m.Data = make(map[string]City)

    m.Data["Ottowa"] = City{"Canada"}
    m.Data["London"] = City{"GB"}
    m.Data["malafya"] = City{"malafya"}

    go m.Round()

    for i := 0; i < 4; i++ {
        go func() {
            time.Sleep(2 * time.Second)
            go m.Add("uz", City{"CityMakon"})
            go m.Add("uzb", City{"CityMakon"})
        }()
    }
    time.Sleep(5 * time.Second)
    done <- struct{}{}
}

la sortie :

----- {Canada}
----- {GB}
----- {malafya}
==================
WARNING: DATA RACE
Write by goroutine 12:
  runtime.mapassign1()
      /usr/lib/golang/src/runtime/hashmap.go:411 +0x0
  main.(*Map).Add()
      /home/narkoz/elixir/round.go:37 +0xaa

Previous write by goroutine 6:
  runtime.mapdelete()
      /usr/lib/golang/src/runtime/hashmap.go:511 +0x0
  main.(*Map).Round()
      /home/narkoz/elixir/round.go:26 +0x3a9

Goroutine 12 (running) created at:
  main.main.func1()
      /home/narkoz/elixir/round.go:54 +0x8c

Goroutine 6 (running) created at:
  main.main()
      /home/narkoz/elixir/round.go:49 +0x2af
==================
----- {CityMakon}
----- {CityMakon}
Found 1 data race(s)
exit status 66

Mais lorsque je change le type de valeur de la carte en int ou string, il n'y a pas de course aux données.

Quelle solution recommandez-vous ?

1voto

ivan.sim Points 5838

MISES À JOUR :

Mais lorsque je change le type de valeur de la carte en int ou string, il n'y a pas de course aux données.

J'ai testé votre code plus avant. Changer le type de valeur de la carte en int ou string continue à produire la condition de course. Essayez de l'exécuter dans un while boucle sur votre coquille, vous verrez ce que je veux dire :

$ while true; do go run -race main.go; done

Il ne devrait pas y avoir de différences entre les types de valeurs.


Comme indiqué par les détecteurs de course, il existe deux conditions de course différentes. La première course (que vous avez corrigée) se produit entre la lecture (de i ) à la ligne 54 et l'écriture (vers i ) à la ligne 51. Cela se produit parce que la fermeture de votre goroutine contient une référence à i qui est modifié par le for dans votre main goroutine. Vous pouvez résoudre ce problème en vous débarrassant de l'option println(">>", i) ou en passant i dans votre fermeture comme ceci :

for i := 0; i < 4; i++ {
  go func(index int) {
    time.Sleep(2 * time.Second)
    println(">>", index)
    go m.Add("uz", City{"CityMakon"})
    go m.Add("uzb", City{"CityMakon"})
  }(i)
}

La deuxième condition de concurrence se produit entre l'affectation de la ligne 37 ( m.Data[id] = h ) et la suppression à la ligne 25 ( delete(m.Data, i) ). Le détecteur de course marque ceci comme une condition de course parce qu'il ne peut pas garantir que l'option Se produire avant sur votre code. Vous pouvez résoudre ce problème de deux façons :

Verrouillage de la delete déclaration :

m.Lock()
delete(m.Data, i)
m.Unlock()

Ou alternativement, extraire les deux cas dans votre Round() en deux méthodes, en passant par le canal :

func (m *Map) Round() {
  for i, v := range m.Data {
    fmt.Println("-----", v)
    delete(m.Data, i)
  }
}

func (m *Map) Done() {
  for range done {
    println("bye")
    break
  }
}

func main() {
  // ...
  go Round()
  go Done()
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X