2017-05-16 1 views
0

Jusqu'à récemment, j'avais supposé que la définition d'un élément d'un List<T> via l'indexeur était thread-safe dans le contexte suivant.C# List <T> Sécurité des threads d'indexeur

// Assumes destination.Count >= source.Count 
static void Function<T,U>(List<T> source, Func<T,U> converter, List<U> destination) 
{ 
    Parallel.ForEach(Partitioner.Create(0, source.Count), range => 
    { 
     for(int i = range.Item1; i < range.Item2; i++) 
     { 
      destination[i] = converter(source[i]); 
     } 
    }); 
} 

Depuis List<T> stocke ses éléments dans un tableau interne et la mise en un par l'indice ne devrait pas nécessiter le redimensionnement, cela semblait être un pas raisonnable de la foi. En regardant le implementation de List<T> dans .NET Core cependant, il semble que le setter de l'indexeur modifie un état interne (voir ci-dessous).

// Sets or Gets the element at the given index. 
public T this[int index] 
{ 
    get 
    { 
     // Following trick can reduce the range check by one 
     if ((uint)index >= (uint)_size) 
     { 
      ThrowHelper.ThrowArgumentOutOfRange_IndexException(); 
     } 
     Contract.EndContractBlock(); 
     return _items[index]; 
    } 

    set 
    { 
     if ((uint)index >= (uint)_size) 
     { 
      ThrowHelper.ThrowArgumentOutOfRange_IndexException(); 
     } 
     Contract.EndContractBlock(); 
     _items[index] = value; 
     _version++; 
    } 
} 

Alors dois-je supposer que List<T> est thread-safe, même si chaque thread ne reçoit que/réglage des éléments de sa propre partie de la collection?

+1

[Quelle est cette chose que vous appelez "thread safe"?] (Https://blogs.msdn.microsoft.com/ericlippert/2009/10/19/what-is-this-thing-you-call-thread -sûr/). La première chose que vous devez faire est de déterminer ce dont vous avez besoin pour être en sécurité. En ce qui concerne le code que vous regardez, je dirais que ce n'est pas sécurisé. Et vous ne devriez certainement pas supposer quoi que ce soit qui ne soit pas documenté en tant que thread-safe. Cependant, notez que le champ '_version' est utilisé pour un détail d'implémentation qui ne vous inquiète pas. Que ce soit modifié de manière imprévisible peut ne pas avoir d'importance pour vous. –

+1

Si vous voulez la sécurité des threads, jetez un coup d'œil à [collections thread-safe] (https://msdn.microsoft.com/en-us/library/dd997305 (v = vs.110) .aspx) – Timo

+0

Bien que vous soyez ce n'est pas vraiment le cas, mais il vaut peut-être mieux éviter de modifier l'état dans une boucle parallèle. Si la situation est que 'converter' est une opération de longue durée, il est préférable de calculer le résultat en utilisant' Parallel', puis d'affecter le résultat avec un seul thread. Essayez ceci: 'statique Liste Fonction (Liste source, Func convertisseur) {return source.AsParallel(). Sélectionnez (s => convertisseur (s)). ToList(); } ' – Enigmativity

Répondre

4

Bonne lecture ici:

https://msdn.microsoft.com/en-us/library/6sh2ey19.aspx#Anchor_10

Pour répondre à votre question, non - selon la documentation, il est pas garanti d'être thread-safe.

Même si l'implémentation actuelle semblait être thread-safe (ce qui n'est pas le cas, de toute façon), ce serait encore une mauvaise idée de faire cette hypothèse. Étant donné que la documentation indique explicitement que les threads ne sont pas sécurisés, les futures versions peuvent légalement modifier l'implémentation sous-jacente afin qu'elle ne soit plus sécurisée par les threads et qu'elle ne casse aucune des hypothèses sur lesquelles vous vous êtes précédemment appuyé.