Je n'ai pas trouvé suffisamment d'informations sur ConcurrentDictionary
types, alors j'ai pensé que je poserais des questions à ce sujet ici.
Actuellement, j'utilise a Dictionary
pour contenir tous les utilisateurs auxquels accède en permanence par plusieurs threads (à partir d'un pool de threads, donc pas de quantité exacte de threads), et il a un accès synchronisé.
J'ai récemment découvert qu'il y avait un ensemble de collections thread-safe dans .NET 4.0, et cela semble très agréable. Je me demandais quelle serait l'option `` plus efficace et plus facile à gérer '', car j'ai le choix entre avoir un Dictionary
accès normal avec un accès synchronisé ou avoir unConcurrentDictionary
qui est déjà thread-safe.
Réponses:
Une collection thread-safe par rapport à une collection non threadsafe peut être considérée différemment.
Envisagez un magasin sans commis, sauf à la caisse. Vous avez une tonne de problèmes si les gens n'agissent pas de manière responsable. Par exemple, disons qu'un client prend une canette d'une pyramide pendant qu'un employé est en train de construire la pyramide, tout l'enfer se déchaînerait. Ou, que se passe-t-il si deux clients atteignent le même article en même temps, qui gagne? Y aura-t-il un combat? Il s'agit d'une collection non threadsafe. Il existe de nombreuses façons d'éviter les problèmes, mais elles nécessitent toutes une sorte de verrouillage, ou plutôt un accès explicite d'une manière ou d'une autre.
D'un autre côté, pensez à un magasin avec un commis à un bureau, et vous ne pouvez faire vos achats que par lui. Vous faites la queue et lui demandez un article, il vous le ramène et vous sortez de la file. Si vous avez besoin de plusieurs articles, vous ne pouvez ramasser qu'autant d'articles à chaque aller-retour que vous vous en souvenez, mais vous devez faire attention à ne pas monopoliser le commis, cela mettra en colère les autres clients en ligne derrière vous.
Considérez ceci maintenant. Dans le magasin avec un employé, que se passe-t-il si vous vous rendez jusqu'au bout de la file et demandez au vendeur «Avez-vous du papier toilette», et il dit «Oui», puis vous dites «Ok, je» Je vous recontacterai quand je saurai combien j'ai besoin », puis au moment où vous serez de retour en tête de file, le magasin pourra bien sûr être épuisé. Ce scénario n'est pas empêché par une collection threadsafe.
Une collection threadsafe garantit que ses structures de données internes sont valides à tout moment, même si elles sont accessibles à partir de plusieurs threads.
Une collection non threadsafe n'offre pas de telles garanties. Par exemple, si vous ajoutez quelque chose à un arbre binaire sur un thread, alors qu'un autre thread est occupé à rééquilibrer l'arbre, il n'y a aucune garantie que l'élément sera ajouté, ou même que l'arbre sera toujours valide par la suite, il pourrait être corrompu au-delà de tout espoir.
Une collection threadsafe ne garantit cependant pas que les opérations séquentielles sur le thread fonctionnent toutes sur le même «instantané» de sa structure de données interne, ce qui signifie que si vous avez un code comme celui-ci:
vous pourriez obtenir une NullReferenceException car entre
tree.Count
ettree.First()
, un autre thread a effacé les nœuds restants dans l'arborescence, ce qui signifieFirst()
que vous retournereznull
.Pour ce scénario, vous devez soit voir si la collection en question dispose d'un moyen sûr d'obtenir ce que vous voulez, soit vous devez peut-être réécrire le code ci-dessus, soit vous devrez peut-être verrouiller.
la source
Dictionary
et la gestion du verrouillage vous-même par rapport à l'utilisation duConcurrentDictionary
type intégré à .NET 4+. Je suis en fait un peu déconcerté que cela ait été accepté.Vous devez toujours être très prudent lorsque vous utilisez des collections thread-safe car cela ne signifie pas que vous pouvez ignorer tous les problèmes de threading. Lorsqu'une collection se présente comme thread-safe, cela signifie généralement qu'elle reste dans un état cohérent même lorsque plusieurs threads lisent et écrivent simultanément. Mais cela ne signifie pas qu'un seul thread verra une séquence "logique" de résultats s'il appelle plusieurs méthodes.
Par exemple, si vous vérifiez d'abord si une clé existe et que vous obtenez ensuite la valeur qui correspond à la clé, cette clé peut ne plus exister même avec une version ConcurrentDictionary (car un autre thread aurait pu supprimer la clé). Vous devez toujours utiliser le verrouillage dans ce cas (ou mieux: combiner les deux appels en utilisant TryGetValue ).
Alors utilisez-les, mais ne pensez pas que cela vous donne un laissez-passer gratuit pour ignorer tous les problèmes de concurrence. Vous devez toujours faire attention.
la source
TryGetValue
a toujours fait partie deDictionary
et a toujours été l'approche recommandée. Les méthodes "concurrentes" importantes quiConcurrentDictionary
introduisent sontAddOrUpdate
etGetOrAdd
. Donc, bonne réponse, mais j'aurais pu choisir un meilleur exemple.En interne, ConcurrentDictionary utilise un verrou distinct pour chaque compartiment de hachage. Tant que vous n'utilisez que Add / TryGetValue et les méthodes similaires qui fonctionnent sur des entrées uniques, le dictionnaire fonctionnera comme une structure de données presque sans verrouillage avec l'avantage de performances doux respectif. OTOH les méthodes d'énumération (y compris la propriété Count) verrouillent tous les compartiments à la fois et sont donc pires qu'un dictionnaire synchronisé, en termes de performances.
Je dirais, utilisez simplement ConcurrentDictionary.
la source
Je pense que la méthode ConcurrentDictionary.GetOrAdd est exactement ce dont la plupart des scénarios multi-threads ont besoin.
la source
Avez-vous vu les extensions réactives pour .Net 3.5sp1. Selon Jon Skeet, ils ont rétroporté un ensemble d'extensions parallèles et de structures de données simultanées pour .Net3.5 sp1.
Il existe un ensemble d'exemples pour .Net 4 Beta 2, qui décrit assez bien comment les utiliser avec les extensions parallèles.
Je viens de passer la semaine dernière à tester le ConcurrentDictionary en utilisant 32 threads pour effectuer des E / S. Cela semble fonctionner comme annoncé, ce qui indiquerait qu'une quantité considérable de tests y a été effectuée.
Éditer : .NET 4 ConcurrentDictionary et modèles.
Microsoft a publié un pdf appelé Patterns of Paralell Programming. Il vaut vraiment la peine d'être téléchargé car il décrit dans de très beaux détails les bons modèles à utiliser pour les extensions .Net 4 Concurrent et les anti-modèles à éviter. C'est ici.
la source
Fondamentalement, vous souhaitez utiliser le nouveau ConcurrentDictionary. Dès la sortie de la boîte, vous devez écrire moins de code pour créer des programmes thread-safe.
la source
C'est
ConcurrentDictionary
une excellente option si elle répond à tous vos besoins en matière de sécurité des threads. Si ce n'est pas le cas, c'est-à-dire que vous faites quelque chose de légèrement complexe, unDictionary
+ normallock
peut être une meilleure option. Par exemple, disons que vous ajoutez des commandes dans un dictionnaire et que vous souhaitez garder à jour le montant total des commandes. Vous pouvez écrire un code comme celui-ci:Ce code n'est pas thread-safe. Plusieurs threads mettant à jour le
_totalAmount
champ peuvent le laisser dans un état corrompu. Vous pouvez donc essayer de le protéger avec unlock
:Ce code est "plus sûr", mais toujours pas thread-safe. Il n'y a aucune garantie que le
_totalAmount
est cohérent avec les entrées du dictionnaire. Un autre thread peut essayer de lire ces valeurs, pour mettre à jour un élément d'interface utilisateur:Le
totalAmount
peut ne pas correspondre au nombre de commandes dans le dictionnaire. Les statistiques affichées peuvent être erronées. À ce stade, vous vous rendrez compte que vous devez étendre lalock
protection pour inclure la mise à jour du dictionnaire:Ce code est parfaitement sûr, mais tous les avantages de l'utilisation d'un
ConcurrentDictionary
ont disparu.Dictionary
, car le verrouillage interne à l'intérieur duConcurrentDictionary
est désormais inutile et redondant.TryAdd
?,AddOrUpdate
?).Mon conseil est donc le suivant: commencez par un
Dictionary
+lock
et conservez la possibilité de passer ultérieurement à uneConcurrentDictionary
optimisation des performances, si cette option est réellement viable. Dans de nombreux cas, ce ne sera pas le cas.la source
Nous avons utilisé ConcurrentDictionary pour la collection mise en cache, qui est re-remplie toutes les 1 heure, puis lue par plusieurs threads clients, similaire à la solution pour cet exemple thread-safe?question.
Nous avons constaté que le changement de ReadOnlyDictionary améliorait les performances globales.
la source