Je veux une implémentation de en List<T>
tant que propriété qui peut être utilisée sans aucun doute en toute sécurité avec les threads.
Quelque chose comme ça:
private List<T> _list;
private List<T> MyT
{
get { // return a copy of _list; }
set { _list = value; }
}
Il semble que j'ai encore besoin de renvoyer une copie (clonée) de la collection, donc si quelque part nous itérons la collection et en même temps la collection est définie, aucune exception n'est soulevée.
Comment implémenter une propriété de collection thread-safe?
IList<T>
(vsList<T>
)?List<T>
met en œuvre? Si oui, pourriez-vous s'il vous plaît fournir une interface dont vous avez besoin au lieu de poser des questions sur tout ce quiList<T>
existe déjà?Réponses:
Si vous ciblez .Net 4, il existe quelques options dans System.Collections.Concurrent Namespace
Vous pouvez utiliser
ConcurrentBag<T>
dans ce cas au lieu deList<T>
la source
ConcurrentBag
est une collection non ordonnée, donc contrairement àList<T>
elle ne garantit pas la commande. Vous ne pouvez pas non plus accéder aux éléments par index.Même s'il a obtenu le plus de votes, on ne peut généralement pas le
System.Collections.Concurrent.ConcurrentBag<T>
remplacerSystem.Collections.Generic.List<T>
tel quel (Radek Stromský l'a déjà souligné) non commandé.Mais il y a une classe appelée
System.Collections.Generic.SynchronizedCollection<T>
qui fait déjà depuis .NET 3.0 partie du framework, mais elle est si bien cachée dans un endroit où l'on ne s'attend pas à ce qu'elle soit peu connue et que vous ne l'ayez probablement jamais trébuché (du moins J'ai jamais fait).SynchronizedCollection<T>
est compilé dans l'assembly System.ServiceModel.dll (qui fait partie du profil client mais pas de la bibliothèque de classes portable).J'espère que cela pourra aider.
la source
Je pense que créer un exemple de classe ThreadSafeList serait facile:
Vous clonez simplement la liste avant de demander un énumérateur, et donc toute énumération fonctionne sur une copie qui ne peut pas être modifiée en cours d'exécution.
la source
T
s'agit d'un type de référence, cela ne retournera-t-il pas simplement une nouvelle liste contenant des références à tous les objets d'origine? Si tel est le cas, cette approche peut encore causer des problèmes de thread car les objets de la liste peuvent être accédés par plusieurs threads via différentes "copies" de la liste.newList
aucun élément n'a été ajouté ou supprimé qui invaliderait l'énumérateur).Même la réponse acceptée est ConcurrentBag, je ne pense pas que ce soit un véritable remplacement de la liste dans tous les cas, comme le dit le commentaire de Radek à la réponse: "ConcurrentBag est une collection non ordonnée, donc contrairement à List, il ne garantit pas la commande. De plus, vous ne pouvez pas accéder aux éléments par index ".
Donc, si vous utilisez .NET 4.0 ou version ultérieure, une solution de contournement pourrait être d'utiliser ConcurrentDictionary avec l'entier TKey comme index de tableau et TValue comme valeur de tableau. C'est une méthode recommandée pour remplacer la liste dans le cours C # Concurrent Collections de Pluralsight . ConcurrentDictionary résout les deux problèmes mentionnés ci-dessus: l'accès aux index et la commande (nous ne pouvons pas nous fier à la commande car c'est une table de hachage sous le capot, mais l'implémentation actuelle de .NET économise l'ordre des éléments ajoutés).
la source
ConcurrentDictionary
est un dictionnaire, pas une liste. 2) Il n'est pas garanti de conserver la commande, comme l'indique votre propre réponse, ce qui contredit la raison pour laquelle vous avez affiché une réponse. 3) Il renvoie à une vidéo sans apporter les citations pertinentes dans cette réponse (ce qui pourrait ne pas être en accord avec leur licence de toute façon).current implementation
si cela n'est pas explicitement garanti par la documentation. La mise en œuvre peut changer à tout moment sans préavis.La
ArrayList
classe de C # a uneSynchronized
méthode.Cela renvoie un wrapper thread-safe autour de toute instance de
IList
. Toutes les opérations doivent être effectuées via le wrapper pour garantir la sécurité du fil.la source
Si vous regardez le code source de List of T ( https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,c66df6f36c131877 ), vous remarquerez qu'il y a une classe là (qui est bien sûr interne - pourquoi, Microsoft, pourquoi?!?!) appelé SynchronizedList of T.Je suis en train de copier-coller le code ici:
Personnellement, je pense qu'ils savaient qu'une meilleure implémentation en utilisant SemaphoreSlim pourrait être créée, mais ils ne l'ont pas fait.
la source
_root
) à chaque accès (lecture / écriture) en fait une solution lente. Il vaut peut-être mieux que cette classe reste interne.Clear()
après un autre appelthis[index]
mais avant que le verrou ne soit activé.index
n'est plus sûr à utiliser et lèvera une exception lors de son exécution finale.Vous pouvez également utiliser le plus primitif
quel verrou utilise (voir cet article C # Verrouiller un objet qui est réaffecté dans le bloc de verrouillage ).
Si vous vous attendez à des exceptions dans le code, ce n'est pas sûr mais cela vous permet de faire quelque chose comme ce qui suit:
Une des bonnes choses à ce sujet est que vous obtiendrez le verrou pour la durée de la série d'opérations (plutôt que de verrouiller chaque opération). Ce qui signifie que la sortie devrait sortir dans les bons morceaux (mon utilisation de cela consistait à obtenir une sortie à l'écran à partir d'un processus externe)
J'aime vraiment la simplicité + la transparence de ThreadSafeList + qui fait l'essentiel pour arrêter les plantages
la source
Dans .NET Core (n'importe quelle version), vous pouvez utiliser ImmutableList , qui a toutes les fonctionnalités de
List<T>
.la source
Je crois
_list.ToList()
que vous en fera une copie. Vous pouvez également l'interroger si vous en avez besoin, par exemple:Quoi qu'il en soit, msdn dit que c'est en effet une copie et pas simplement une référence. Oh, et oui, vous devrez verrouiller la méthode set comme les autres l'ont souligné.
la source
Il semble que beaucoup de personnes qui trouvent cela souhaitent une collection de taille dynamique indexée thread-safe. La chose la plus proche et la plus simple que je connaisse serait.
Cela vous obligerait à vous assurer que votre clé est correctement incriminée si vous souhaitez un comportement d'indexation normal. Si vous faites attention, .count peut suffire comme clé pour toute nouvelle paire valeur / clé que vous ajoutez.
la source
Je suggérerais à toute personne traitant d'un
List<T>
scénario multi-threading de jeter un coup d'œil aux collections immuables, en particulier l' ImmutableArray .Je l'ai trouvé très utile lorsque vous avez:
Peut également être utile lorsque vous devez implémenter une sorte de comportement de type transaction (c.-à-d. Annuler une opération d'insertion / mise à jour / suppression en cas d'échec)
la source
Voici le cours que vous avez demandé:
la source
this.GetEnumerator();
quand @Tejs le suggèrethis.Clone().GetEnumerator();
?[DataContract( IsReference = true )]
?Fondamentalement, si vous souhaitez énumérer en toute sécurité, vous devez utiliser lock.
Veuillez vous référer à MSDN à ce sujet. http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx
Voici une partie de MSDN qui pourrait vous intéresser:
Les membres statiques publics (partagés dans Visual Basic) de ce type sont thread-safe. Les membres d'instance ne sont pas garantis d'être thread-safe.
Une liste peut prendre en charge plusieurs lecteurs simultanément, tant que la collection n'est pas modifiée. L'énumération via une collection n'est pas intrinsèquement une procédure thread-safe. Dans les rares cas où une énumération est en conflit avec un ou plusieurs accès en écriture, le seul moyen de garantir la sécurité des threads est de verrouiller la collection pendant toute l'énumération. Pour permettre l'accès à la collection par plusieurs threads pour la lecture et l'écriture, vous devez implémenter votre propre synchronisation.
la source
Voici la classe pour la liste thread-safe sans verrou
la source
Utilisez l'
lock
instruction pour ce faire. ( Lisez ici pour plus d'informations. )Pour votre information, ce n'est probablement pas exactement ce que vous demandez - vous voulez probablement verrouiller plus loin dans votre code, mais je ne peux pas le supposer. Jetez un œil à
lock
mot clé et adaptez son utilisation à votre situation spécifique.Si vous en avez besoin, vous pouvez
lock
à la fois bloquerget
etset
utiliser la_list
variable qui ferait en sorte qu'une lecture / écriture ne puisse pas se produire en même temps.la source
lock
déclaration. En utilisant un exemple similaire, je pourrais affirmer que nous ne devrions pas utiliser de boucles for, car vous pourriez bloquer l'application avec pratiquement aucun effort:for (int x = 0; x >=0; x += 0) { /* Infinite loop, oops! */ }
lock (this)
etlock (typeof(this))
sont de grands non-non.