174 votes

Propriété Thread-safe List<T>

Je veux une implémentation de List<T> comme une propriété qui peut être utilisée en toute sécurité sans aucun doute.

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 retourner une copie (clonée) de la collection de sorte que si quelque part nous itérons la collection et en même temps la collection est définie, alors aucune exception n'est levée.

Comment mettre en œuvre une propriété de collection à sécurité thread ?

6 votes

Utiliser des verrous, ça devrait le faire.

0 votes

Peut-on utiliser une implémentation thread-safe de IList<T> (vs List<T> ) ?

3 votes

Avez-vous vérifié Collection synchronisée<T> ?

227voto

Bala R Points 57552

Si vous ciblez .Net 4, il y a quelques options dans la section System.Collections.Concurrent Espace de nommage

Vous pourriez utiliser ConcurrentBag<T> dans ce cas, au lieu de List<T>

9 votes

Comme List<T> et contrairement à Dictionary, ConcurrentBag accepte les doublons.

165 votes

ConcurrentBag est une collection non ordonnée, donc contrairement à List<T> il ne garantit pas la commande. Vous ne pouvez pas non plus accéder aux éléments par index.

15 votes

@RadekStromský a raison, et dans le cas où vous voulez une liste concurrente ordonnée, vous pourriez essayer ConcurrentQueue (FIFO) o ConcurrentStack (LIFO) .

122voto

chha Points 180

Même s'il a obtenu le plus grand nombre de votes, on ne peut généralement pas prendre System.Collections.Concurrent.ConcurrentBag<T> en tant que remplacement à sécurité thread pour System.Collections.Generic.List<T> car il n'est (Radek Stromský l'a déjà signalé) pas commandé.

Mais il y a une classe appelée System.Collections.Generic.SynchronizedCollection<T> qui fait déjà partie du cadre de travail depuis .NET 3.0, mais il est si bien caché dans un endroit où l'on ne s'y attend pas qu'il est peu connu et que vous n'êtes probablement jamais tombé dessus (en tout cas, je ne l'ai jamais fait).

SynchronizedCollection<T> est compilé dans l'assemblage System.ServiceModel.dll (qui fait partie du profil client mais pas de la bibliothèque de classes portable).

5 votes

Je pleure que cela ne soit pas dans la librairie principale :{ Une simple collection synchronisée est souvent tout ce qui est nécessaire.

1 votes

Discussion supplémentaire utile sur cette option : stackoverflow.com/a/4655236/12484

4 votes

Et non disponible dans .net core

22voto

Tejs Points 23834

Je pense qu'il serait facile de créer un exemple de classe ThreadSafeList :

public class ThreadSafeList<T> : IList<T>
{
    protected List<T> _internalList = new List<T>();

    // Other Elements of IList implementation

    public IEnumerator<T> GetEnumerator()
    {
        return Clone().GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return Clone().GetEnumerator();
    }

    protected static object _lock = new object();

    public List<T> Clone()
    {
        List<T> newList = new List<T>();

        lock (_lock)
        {
            _internalList.ForEach(x => newList.Add(x));
        }

        return newList;
    }
}

Vous clonez simplement la liste avant de demander un énumérateur, et ainsi toute énumération fonctionne à partir d'une copie qui ne peut pas être modifiée pendant l'exécution.

1 votes

N'est-ce pas un clone superficiel ? Si T est un type de référence, ne retournera-t-il pas simplement une nouvelle liste contenant des références à tous les objets originaux ? Si c'est le cas, cette approche pourrait encore causer des problèmes de threading puisque les objets de la liste pourraient être accédés par plusieurs threads à travers différentes "copies" de la liste.

4 votes

Correct, c'est une copie superficielle. Le but était simplement d'avoir un ensemble cloné sur lequel on pourrait itérer en toute sécurité (donc newList n'a pas d'éléments ajoutés ou supprimés qui invalideraient le recenseur).

0 votes

Bien reçu. C'est une solution vraiment élégante à ce problème. Après avoir sérieusement réfléchi et cherché sur Google, je n'ai rien trouvé d'autre qui s'en approche en termes d'équilibre sécurité/simplicité. Merci de l'avoir publiée !

0voto

Jonathan Henson Points 4602

Je crois _list.ToList() vous fera une copie. Vous pouvez également l'interroger si vous en avez besoin, par exemple :

_list.Select("query here").ToList(); 

Quoi qu'il en soit, msdn dit qu'il s'agit bien d'une copie et non d'une simple référence. Oh, et oui, vous aurez besoin de verrouiller la méthode set comme les autres l'ont souligné.

-3voto

Ilho Points 625

Fondamentalement, si vous voulez énumérer en toute sécurité, vous devez utiliser lock.

Veuillez consulter le MSDN à ce sujet. http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx

Voici une partie de MSDN qui pourrait vous intéresser :

Les membres publics statiques (Shared en Visual Basic) de ce type sont thread safe. Les membres de l'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 dans une collection n'est pas intrinsèquement une procédure thread-safe. Dans les rares cas où une énumération est confrontée à un ou plusieurs accès en écriture, la seule façon d'assurer la sécurité des threads est de verrouiller la collection pendant toute l'énumération. Pour permettre à plusieurs threads d'accéder à la collection en lecture et en écriture, vous devez implémenter votre propre synchronisation.

2 votes

Ce n'est pas du tout le cas. Vous pouvez utiliser des ensembles simultanés.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X