Comment utiliser RWMutex dans Golang?
type Stat struct {
counters map[string]*int64
countersLock sync.RWMutex
averages map[string]*int64
averagesLock sync.RWMutex
}
Il est appelé ci-dessous
func (s *Stat) Count(name string) {
s.countersLock.RLock()
counter := s.counters[name]
s.countersLock.RUnlock()
if counter != nil {
atomic.AddInt64(counter, int64(1))
return
}
}
Ma compréhension est que nous avons d'abord verrouiller le récepteur s (qui est un type de Stat) et puis nous ajoutons à cela si le compteur n'existe pas.
Questions:
Q1: pourquoi avons-nous besoin de le verrouiller? Ce n' RWMutex
même dire?
T2: s.countersLock.RLock()
- est-ce à verrouiller l'ensemble du récepteur s ou seulement les compteurs champ type de Stat?
T3: s.countersLock.RLock()
- est-ce lock-up des moyennes de champ?
Q4: Pourquoi devrions-nous utiliser RWMutex
? Je pensais que le canal était la meilleure façon de gérer la simultanéité dans Golang?
Q5: Qu'est-ce atomic.AddInt64
. Pourquoi avons-nous besoin atomique dans ce cas?
Q6: Pourquoi serait-on débloquer juste avant de les ajouter au?
Vous devez vous connecter pour publier un commentaire.
RW signifie en Lecture/Écriture. CF doc: http://golang.org/pkg/sync/#RWMutex.
Nous avons besoin de le verrouiller pour empêcher d'autres routines/thread pour modifier la valeur, tandis que nous les traitons.
Comme un mutex, le verrouillage se produit uniquement lorsque vous appelez la
RLock()
fonction. Si d'autres goroutine déjà appelé leWLock()
, puis il bloque. Vous pouvez appeler n'importe quel numéro deRLock()
au sein de la même goroutine, il ne ferme pas à clé.De sorte qu'il ne bloque pas les autres champs, pas même
s.counters
. Dans votre exemple, vous pouvez verrouiller la carte de recherche pour trouver le bon guichet.Non, comme dit dans T2, un
RLock
verrouille uniquement lui-même.Canal est très utile, mais parfois, il n'est pas assez et parfois, il ne fait pas de sens.
Ici, comme vous le verrouillage de la carte d'accès, un mutex de sens. Avec un chan, vous devez avoir un tampon chan de 1, envoyer de l'avant et de recevoir après avoir. Pas très intuitif.
Cette fonction incrémente la variable donnée dans une façon atomique. Dans votre cas, vous avez une condition de course:
counter
est un pointeur et la variable réelle peut être détruit après la sortie de l'écluse et avant l'appel àatomic.AddInt64
.Si vous n'êtes pas familier avec ce genre de choses, je vous conseille de coller avec les Mutex et de faire tous les traitements dont vous avez besoin dans entre le verrouillage/déverrouillage.
Vous ne devriez pas.
Je ne sais pas ce que vous essayez de faire, mais ici, c'est une (simple) exemple: https://play.golang.org/p/cVFPB-05dw
The lock can be held by an arbitrary number of readers or a single writer.
-race
(qui n'a pas été détecté de retour en 2013) est en ce qui concerne l'impression finale. Le mutex fonctionne comme prévu dans le increaseCounter fonction. Voici l'exemple avec un petit correctif: play.golang.org/p/cVFPB-05dwLorsque plusieurs threads* doit muter la même valeur, un mécanisme de verrouillage est nécessaire pour se synchronise accès. Sans elle, deux ou plusieurs threads* peut être écrit à la même valeur dans le même temps, résultant en une corruption de la mémoire qui résulte généralement d'un crash.
La atomique paquet fournit un moyen rapide et facile pour synchroniser l'accès à des valeurs primitives. Pour un compteur, il est le plus rapide de la méthode de synchronisation. Il a des méthodes bien définies de cas d'utilisation, tels que l'incrémentation, décrémentation, des échanges, etc.
La synchronisation paquet fournit un moyen de synchroniser l'accès plus compliqué de valeurs, telles que des cartes, des tranches, des tableaux, ou des groupes de valeurs. Vous utilisez ce pour les cas d'utilisation qui ne sont pas définis dans atomique.
Dans les deux cas, le verrouillage est nécessaire uniquement lors de l'écriture. Plusieurs threads* sans danger de lire la même valeur, sans un mécanisme de verrouillage.
Permet de jeter un coup d'oeil au code que vous avez fournis.
Ce qui manque ici est de savoir comment la carte est eux-mêmes sont initialisés. Et jusqu'à présent, les cartes ne sont pas muté. Si le compteur noms sont déterminés à l'avance et ne peuvent pas être ajoutées plus tard, vous n'avez pas besoin de la RWMutex. Ce code pourrait ressembler à quelque chose comme ceci:
(Note: j'ai supprimé des moyennes car il n'était pas utilisé dans l'exemple original.)
Maintenant, disons que vous ne voulez pas que vos marqueurs pour être prédéterminé. Dans ce cas, vous auriez besoin d'un mutex pour synchroniser l'accès.
Permet d'essayer avec juste un Mutex. C'est simple, car un seul thread* peut contenir Verrouillage à la fois. Si un deuxième thread* essaie de Verrouillage avant les premières versions d'eux avec Déverrouiller, il attend (ou blocs)** jusqu'alors.
Le code ci-dessus fonctionne très bien. Mais il y a deux problèmes.
Le problème n ° 1 est facile à résoudre. Utilisation reporter:
Cela garantit que Unlock() est toujours appelé. Et si pour une raison quelconque vous avez plus d'un retour, vous avez seulement besoin de spécifier de Déverrouillage() une fois à la tête de la fonction.
Problème n ° 2 peut être résolu avec RWMutex. Comment ça fonctionne exactement, et pourquoi est-il utile?
RWMutex est une extension de Mutex et ajoute deux méthodes: RLock et RUnlock. Il y a quelques points qui sont importants à noter à propos de RWMutex:
RLock est partagée verrou en lecture. Lorsqu'un verrou est pris avec elle, les autres threads* peuvent aussi faire leur propre cadenas avec RLock. Cela signifie que plusieurs threads* peut lire en même temps. Il est semi-exclusif.
Si le mutex est de lire verrouillé, un appel à Verrouillage est bloqué**. Si un ou plusieurs lecteurs de placer un verrou, vous ne pouvez pas écrire.
Si le mutex est d'écrire verrouillé (avec Verrouillage), RLock bloquera**.
Une bonne façon de penser est RWMutex est un Mutex avec un lecteur de compteur. RLock incrémente le compteur tout en RUnlock décrémente. Un appel à Verrouillage bloquera tant que le compteur est > 0.
Vous pensez peut-être: Si ma demande est de lire lourds, que cela signifierait un écrivain peut être bloqué indéfiniment? Pas de. Il y a une propriété utile de RWMutex:
Penser comme la lumière au-dessus d'un registre à l'épicerie qui dit que la caisse est ouverte ou pas. Les gens à la ligne arrive à rester là et ils seront aidés, mais de nouvelles personnes ne peuvent pas obtenir en ligne. Dès que le dernier client est aidé la caisse va sur pause, et que le registre soit reste fermé jusqu'à ce qu'ils reviennent ou qu'ils sont remplacés par une autre caisse.
Permet de modifier l'exemple précédent avec une RWMutex:
Avec le code ci-dessus j'ai séparé la logique dans
getCounter
etinitCounter
fonctions:Le code ci-dessus, à la différence de la Mutex exemple, vous permet d'incrémenter les compteurs différents simultanément.
Une autre chose que je voulais souligner est que, avec tous les exemples ci-dessus, la carte
map[string]*int64
contient des pointeurs vers les compteurs à zéro, de ne pas les compteurs eux-mêmes. Si vous devez stocker les compteurs de la cartemap[string]int64
vous devez utiliser Mutex sans atomique. Ce code devrait ressembler à quelque chose comme ceci:Vous pouvez faire pour réduire la collecte des ordures ménagères - mais ce ne importe si vous avait des milliers de compteurs et les compteurs eux-mêmes ne prennent pas beaucoup d'espace (par rapport à quelque chose comme un tampon d'octets).
*
Quand je dis thread je veux dire allez-routine. Un thread dans d'autres langues est un mécanisme pour l'exécution d'un ou de plusieurs programmes simultanément. Un thread est coûteux de créer et de démontage. Un aller-routine est construite au-dessus des fils, mais le ré-utilise. Lorsqu'un aller-routine dort sous-jacents fil peut être utilisé par un autre aller-routine. Lorsqu'un aller-routine se réveille, il pourrait être sur un thread différent. Aller gère tout cela derrière les coulisses. - Mais pour toutes fins utiles, vous le feriez à un aller-routine comme un fil quand il s'agit de l'accès à la mémoire. Cependant, vous n'avez pas à être prudente lors de l'utilisation de go-routines que vous ne threads.**
Lorsqu'un aller-routine est bloqué parLock
,RLock
, un canal, ou Le sommeil, le thread peut être ré-utilisé. Pas de cpu est utilisé par qui vont-routine - il pense que l'attente en ligne. Comme les autres langues une boucle infinie commefor {}
bloquerait tout en gardant le cpu et la routine occupé pense que comme courir dans un cercle - vous obtiendrez des étourdissements, de vomir, et les gens autour de vous ne sera pas très heureux.