Dans le contexte de cette déclaration, P>
Un dictionnaire peut supporter Plusieurs lecteurs simultanément, aussi longtemps Comme la collection n'est pas modifiée. Même si, énumérant à travers un la collecte n'est intrinsèquement pas un Procédure de fil-sécurité. Dans les rares cas où une énumération soutient avec accès à l'écriture, la collection doit être verrouillé pendant toute la énumération. Permettre la collection à accéder par plusieurs threads pour lecture et écriture, vous devez Mettre en œuvre votre propre synchronisation. P> blockQuote>
Que fait et écrire signifie? Ma compréhension est qu'une lecture est une opération qui recherche une clé et fournit une référence à sa valeur et qu'une écriture est une opération qui ajoute ou supprime une paire de valeur clé du dictionnaire. Cependant, je ne trouve rien de concluant que pour cela. P>
La grande question est donc la suivante, lors de la mise en œuvre d'un dictionnaire de fil de fil, une opération qui mettrait à jour la valeur d'une clé existante dans le dictionnaire soit considérée comme un lecteur ou un écrivain? Je prévois d'avoir plusieurs threads accédant à des touches uniques dans un dictionnaire et de modifier leurs valeurs, mais les threads ne vont pas ajouter / supprimer de nouvelles touches. p>
L'implication évidente, supposant que la modification d'une valeur existante n'est pas une opération d'écriture sur le dictionnaire, est que ma mise en œuvre de la diction diction de fil peut être beaucoup plus efficace, car je n'aurais pas besoin d'obtenir une serrure exclusive à chaque fois que je Essayez de mettre à jour la valeur à une clé existante. p>
L'utilisation de la simultindication de .net 4.0 n'est pas une option. p>
6 Réponses :
Écraser une valeur existante doit être traitée comme une opération d'écriture p>
C'est vraiment un commentaire, pas une réponse à la question. Veuillez utiliser "Ajouter un commentaire" pour laisser des commentaires pour l'auteur.
@Sliverninja, me semble que cette réponse aborde la question principale "une opération qui met à jour la valeur d'une clé existante dans le dictionnaire soit considérée comme un lecteur ou un écrivain?".
Modification d'une valeur est une écriture et introduit une condition de course. P>
Disons la valeur initiale de MyDICT [5] = 42. Un thread met à jour le myDICT [5] à 112. Un autre fil met à jour le myDICK [5] à 837. P>
Quelle devrait être la valeur de la mydict [5] à la fin? L'ordre des threads est important dans ce cas, ce qui signifie que vous devez vous assurer que la commande est explicite ou qu'elles n'écrivent pas. P>
Ce que vous décrivez n'est pas vraiment une condition de race - puisque bien de toute la manière qui le fait la dernière victoire - et ce serait identique si vous avez enfermé l'accès. Une condition de race serait quelque chose comme: si (! Dict.containskey (5)) dict (5, nouvelle valeur ()), il existe maintenant une incohérence potentielle générée.
@Neil Je pense que c'est une condition de race, juste pas une condition de race que le dictionnaire peut traiter. Je code déjà en place pour garantir que le type de condition de race mentionnée dans la réponse ne se produise pas.
@Joshua - une condition de course pourrait se produire dans le code du dictionnaire qui implémente 'myDICT [5] = x' mais il n'y a pas d'état de course existant entre les instructions de Serinus '3 définies. Qui a déjà défini la valeur la dernière victoire - c'est juste une description de la programmation normale. Une condition de course se produit dans le cas où vous définissez l'état basé sur l'examen précédemment précédemment. Sur son propre x = 1 ne peut pas générer une condition de course x ++ peut.
Tout ce qui peut affecter les résultats d'une autre lecture doit être considéré comme une écriture. p>
Changer la clé d'une clé est très définitivement une écriture car elle provoquera que l'élément se déplace dans le hachage interne ou l'index ou si les dictionnaires font leurs trucs O (log (n)) ... P>
Ce que vous voudrez peut-être faire est de regarder ReaderWriterLock P>
http://msdn.microsoft.com/fr- US / Bibliothèque / System.threading.ReaderWriterLock.aspx P>
Une grande partie de la documentation que j'avais lu a montré le lecteur de lecture / écrivain d'avoir une performance choquante médiocre, pire que d'utiliser une seule serrure. Il semble que ReaderWriterLockSlim devrait être ce que vous utilisez s'il est disponible ou utilisez autre chose. msdn.microsoft.com/en-us/Library/... < / a>
@Erik -i est d'accord, mais ajouterais: c'est toujours un équilibre. Il faudrait dépendre des autres coûts dans le mélange. Si la quantité de lecture et d'écriture est à peu près égale, je vous conseillerais simplement d'utiliser le verrou () - mais si l'écriture est rare - et c'est généralement le verrouillage de l'écrivain Lecteur est probablement bien. Il sera facile de passer à la serrure mince lors de la mise à niveau. Profilage lorsque vous avez un problème, c'est le meilleur moyen d'obtenir la performance. Très souvent, les gens passent des jours à coder pour sauver des millisecondes par opération.
D'accord, parfois les développeurs dépensent trop d'essayer de pré-optimiser. J'ai utilisé la phrase "performance choquante médiocre" pour noter qu'il semble ne pas être une question de performance subtile, mais plutôt drastique. Il est généralement considéré comme meilleur pour l'éviter plutôt que d'l'utiliser. Au moins, cela semble être une grande partie des conclusions que je vois atteintes lors de diverses discussions sur la performance.
Mise à jour d'une valeur est conceptuellement une opération d'écriture. Lors de la mise à jour d'une valeur avec un accès simultané où une lecture est effectuée avant la fin d'une écriture, vous lisez une vieille valeur. Lorsque deux écrit un conflit, la mauvaise valeur peut être stockée. P>
L'ajout d'une nouvelle valeur pourrait déclencher une croissance du stockage sous-jacent. Dans ce cas, la nouvelle mémoire est allouée, tous les éléments sont copiés dans la nouvelle mémoire, le nouvel élément est ajouté, l'objet Dictionnaire est mis à jour pour se reporter à la nouvelle localisation de la mémoire pour le stockage et l'ancienne mémoire est libérée et disponible pour la collecte des ordures. Pendant ce temps, plus d'écritures pourraient causer un gros problème. Deux écrit en même temps pourraient déclencher deux instances de cette copie de la mémoire. Si vous suivez via la logique, vous verrez un élément se perdre car seul le dernier fil à mettre à jour la référence connaîtra sur les éléments existants et non les autres éléments qui essayaient d'être ajoutés. P>
C'est un point intéressant, si je déclare un dictionnaire de
Pour Dictionnaire
De plus, l'insertion lancera une exception si vous essayez d'insérer la même clé deux fois. Cela peut se produire facilement dans un environnement multi-fileté où le verrouillage n'était pas correctement mis en œuvre. Si vous voulez être plus sûr, il suffit d'accéder à une clé inexistante le fera de créer. Si cela existe, il sera mis à jour. Pas de lancement d'exception. Utilisez INSERT si l'ajout de la même clé est une condition d'erreur réelle que vous souhaitez attraper. annuaire.insert ("Key1", "Bonjour"); Versus Directory ["Key1"] = "Bonjour";
Une opération de lecture est tout ce qui obtient une clé ou une valeur à partir d'un dictionnaire code>, une opération d'écriture est tout ce qui met à jour ou ajoute une clé ou une valeur. Donc, un processus mettant à jour une clé serait considéré comme un écrivain.
Un moyen simple de faire un dictionnaire Safe de thread est de créer votre propre implémentation de la marque code> code> qui verrouille simplement un mutex puis transmet le Appelez à une implémentation: p>
public class MyThreadSafeDictionary<T, J> : IDictionary<T, J> { private object mutex = new object(); private IDictionary<T, J> impl; public MyThreadSafeDictionary(IDictionary<T, J> impl) { this.impl = impl; } public void Add(T key, J value) { lock(mutex) { impl.Add(key, value); } } // implement the other methods as for Add }
Y a-t-il des raisons de faire cela plutôt que d'utiliser une concurrence concurrente?
Un point majeur non encore mentionné est que si une classe utilitaire utile utile dans des cas où toutes les clés associées à un dictionnaire seront connues À l'avance du code qui doit utiliser, c'est le cas: P> TVALUE code> est un type de classe, les éléments détenus par un dictionnaire
TVALUE code> Objets em>. Si on reçoit une référence du dictionnaire, le dictionnaire ne connaîtra ni ne se souciera de quoi que ce soit peut ne pas faire avec l'objet qui l'a fait référence à ce que l'objet mentionné ainsi.
class MutableValueHolder<T>
{
public T Value;
}