Maison  >  Article  >  développement back-end  >  Pourquoi l'ajout à une tranche dans des goroutines simultanées n'est-il pas thread-safe ?

Pourquoi l'ajout à une tranche dans des goroutines simultanées n'est-il pas thread-safe ?

Linda Hamilton
Linda Hamiltonoriginal
2024-11-10 00:49:02762parcourir

Why is Appending to a Slice in Concurrent Goroutines Not Thread-Safe?

Tranches simultanées : ajout non thread-safe

Question :

Considérez le code suivant qui s'ajoute à une tranche à l'aide de plusieurs goroutines dans une boucle for :

destSlice := make([]myClass, 0)

var wg sync.WaitGroup
for _, myObject := range sourceSlice {
    wg.Add(1)
    go func(closureMyObject myClass) {
        defer wg.Done()
        var tmpObj myClass
        tmpObj.AttributeName = closureMyObject.AttributeName
        destSlice = append(destSlice, tmpObj)
    }(myObject)
}
wg.Wait()

À l'occasion, ce code produit des données manquantes ou vides dans destSlice ou n'inclut pas tous les éléments de sourceSlice. Pourquoi cela pourrait-il se produire ?

Réponse :

Dans Go, toutes les valeurs sont sensibles aux courses de données lorsqu'elles sont soumises à des opérations de lecture/écriture simultanées, y compris les tranches. Cela est dû à la façon dont les en-têtes de tranche (qui contiennent les informations sur la capacité et la longueur de la tranche) sont implémentés.

L'exécution du code avec l'indicateur -race confirmera la présence de courses de données :

go run -race play.go

Solution :

Pour éviter les courses de données et garantir la sécurité simultanée lors de l'ajout à une tranche, une primitive de synchronisation telle qu'un sync.Mutex doit être utilisée pour protéger le écriture de la valeur destSlice :

var (
    mu        = &sync.Mutex{}
    destSlice = make([]myClass, 0)
)

var wg sync.WaitGroup
for _, myObject := range sourceSlice {
    wg.Add(1)
    go func(closureMyObject myClass) {
        defer wg.Done()
        var tmpObj myClass
        tmpObj.AttributeName = closureMyObject.AttributeName
        mu.Lock()
        destSlice = append(destSlice, tmpObj)
        mu.Unlock()
    }(myObject)
}
wg.Wait()

Vous pouvez également envisager d'utiliser des canaux pour faciliter le processus d'ajout simultané.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn