Selon le blog Go,
Les cartes ne sont pas sûres pour une utilisation simultanée: ce qui se passe lorsque vous leur lisez et leur écrivez simultanément n'est pas défini. Si vous avez besoin de lire et d'écrire sur une carte à partir de goroutines exécutant simultanément, les accès doivent être transmis via un type de mécanisme de synchronisation . (source: https://blog.golang.org/go-maps-in-action )
Quelqu'un peut-il en dire plus? Les opérations de lecture simultanées semblent autorisées pour toutes les routines, mais les opérations de lecture/écriture simultanées peuvent générer une condition de concurrence critique si vous essayez de lire et d'écrire sur la même clé.
Ce dernier risque peut-il être réduit dans certains cas? Par exemple:
Ce n'est pas du code (évidemment) mais je pense que cela montre les grandes lignes d'un cas où même si A et B tentent tous deux d'accéder à m, il n'y aura pas de condition de concurrence critique, contraintes supplémentaires.
Avant Golang 1.6, la lecture simultanée est correcte, l'écriture simultanée n'est pas correcte, mais l'écriture et la lecture simultanée sont correctes. Depuis Golang 1.6, la carte ne peut pas être lue lorsqu’elle est en cours d’écriture ... Donc, après Golang 1.6, la carte d’accès simultané devrait ressembler à:
package main
import (
"sync"
"time"
)
var m = map[string]int{"a": 1}
var lock = sync.RWMutex{}
func main() {
go Read()
time.Sleep(1 * time.Second)
go Write()
time.Sleep(1 * time.Minute)
}
func Read() {
for {
read()
}
}
func Write() {
for {
write()
}
}
func read() {
lock.RLock()
defer lock.RUnlock()
_ = m["a"]
}
func write() {
lock.Lock()
defer lock.Unlock()
m["b"] = 2
}
Ou vous obtiendrez l'erreur ci-dessous:
AJOUTÉE:
Vous pouvez détecter la course en utilisant go run -race race.go
Changer la fonction read
:
func read() {
// lock.RLock()
// defer lock.RUnlock()
_ = m["a"]
}
Un autre choix:
Comme nous le savions, map
a été implémenté par des compartiments et sync.RWMutex
verrouille tous les compartiments. concurrent-map utilise fnv32
pour partager la clé et chaque compartiment utilise un sync.RWMutex
.
La lecture simultanée (lecture seule) est correcte. L'écriture et/ou la lecture simultanée n'est pas correcte.
Plusieurs goroutines peuvent uniquement écrire et/ou lire la même carte si l’accès est synchronisé, par exemple. via le paquet sync
, avec des canaux ou par d’autres moyens.
Votre exemple:
- La fonction A génère k et définit m [k] = 0. C’est la seule fois où A écrit pour mapper m. k est connu pour ne pas être en m.
- A passe k à la fonction B s'exécutant simultanément
- A lit alors m [k]. Si m [k] == 0, il attend, ne continuant que lorsque m [k]! = 0
- B cherche k sur la carte. S'il le trouve, B définit m [k] sur un entier positif. Si ce n'est pas le cas, attend que k soit dans m.
Votre exemple comporte 2 goroutines: A et B, et A tente de lire m
(à l'étape 3) et B tente de l'écrire (à l'étape 4) simultanément. Il n'y a pas de synchronisation (vous n'en avez mentionné aucune), donc cela seul n'est pas autorisé/non déterminé.
Qu'est-ce que ça veut dire? Non déterminé signifie que même si B écrit m
, A ne peut jamais observer le changement. Ou A peut observer un changement qui ne s'est même pas produit. Ou une panique peut survenir. Ou bien la Terre pourrait exploser à cause de cet accès simultané non synchronisé (bien que la probabilité de ce dernier cas soit extrêmement petite, voire inférieure à 1e-40).
Questions connexes:
Qu'est-ce que ne pas être thread-safe signifie sur les cartes dans Go?
Quel est le danger de négliger goroutine/thread-safety lorsque vous utilisez une carte dans Go?
Le moteur d’exécution a ajouté une détection légère, au meilleur des efforts, du concurrent simultané mauvaise utilisation des cartes. Comme toujours, si un goroutine écrit sur une carte, non. L’autre goroutine devrait lire ou écrire la carte simultanément. Si le moteur d'exécution détecte cette condition, imprime un diagnostic et se bloque le programme. Le meilleur moyen d'en savoir plus sur le problème est de lancer le programme sous le détecteur de course, qui identifiera de manière plus fiable la course et donner plus de détails.
Les cartes sont des structures de données complexes, auto-réorganisées. Les accès simultanés en lecture et en écriture ne sont pas définis.
Sans code, il n'y a pas grand chose à dire.
Comme indiqué dans les autres réponses, le type natif map
n'est pas goroutine
- safe. Quelques notes après avoir lu les réponses actuelles:
concurrent-map
here qui tient compte des performances et de la convivialité. Vous pouvez l'utiliser pour gérer les problèmes de concurrence pour vous. Après de longues discussions, il a été décidé que l'utilisation typique des cartes ne nécessitait pas un accès sécurisé de plusieurs goroutines. Dans les cas où cela était le cas, la carte faisait probablement partie d'une structure de données plus grande ou d'un calcul déjà synchronisé. Par conséquent, exiger que toutes les opérations de carte utilisent un mutex ralentirait la plupart des programmes et augmenterait la sécurité de quelques-uns. Ce n’était cependant pas une décision facile, car cela signifiait que l’accès incontrôlé aux cartes pouvait bloquer le programme.
Le langage n'exclut pas les mises à jour de cartes atomiques. Lorsque cela est nécessaire, par exemple lors de l'hébergement d'un programme non approuvé, la mise en œuvre peut verrouiller l'accès aux cartes.
L'accès à la carte n'est dangereux que lorsque des mises à jour sont effectuées. Tant que toutes les goroutines ne font que lire - rechercher des éléments dans la carte, y compris en itérant à l'aide d'une boucle for range - et ne pas modifier la carte en attribuant des éléments ou en supprimant des éléments, il est prudent pour elles d'accéder simultanément à la carte sans synchronisation.
Pour aider à corriger l'utilisation de la carte, certaines implémentations de la langue contiennent une vérification spéciale qui signale automatiquement au moment de l'exécution qu'une carte est modifiée de manière non sécurisée par une exécution simultanée.
Vous pouvez stocker un pointeur sur un int de la carte et faire en sorte que plusieurs goroutines lisent l’intérêt pointé pendant qu’un autre écrit une nouvelle valeur sur l’entier. La carte n'est pas mise à jour dans ce cas.
Ce ne serait pas idiomatique pour Go et pas ce que vous demandiez.
Ou au lieu de transmettre une clé à une carte, vous pouvez transmettre l'index à un tableau et le faire mettre à jour par un goroutine pendant que d'autres lisent l'emplacement.
Mais vous vous demandez probablement pourquoi la valeur d'une carte ne peut pas être mise à jour avec une nouvelle valeur alors que la clé est déjà dans la carte. Vraisemblablement, rien dans le schéma de hachage de la carte n'a été modifié - du moins, compte tenu de sa mise en œuvre actuelle. Il semblerait que les auteurs de Go ne veuillent pas prendre en compte de tels cas particuliers. Généralement, ils veulent que le code soit facile à lire et à comprendre, et une règle telle que ne pas autoriser l'écriture sur carte alors que d'autres goroutines sont en train de lire simplifie les choses. Désormais, ils peuvent même commencer à détecter les utilisations abusives au cours de l'exécution normale, permettant ainsi à de nombreuses personnes débogage.