2010-05-02 6 views
6

Je rencontre des problèmes avec Parallel for Loops et l'ajout à une liste. Le problème est que le même code peut générer une sortie différente à différents moments. J'ai mis en place un code de test ci-dessous. Dans ce code, je crée une liste de 10 000 valeurs int. 1/10ème des valeurs sera 0, 1/10ème des valeurs sera 1, jusqu'à 1/10ème des valeurs étant 9.Parallel For Loop - Problèmes lors de l'ajout à une liste

Après avoir configuré cette liste, j'ai configuré une boucle parallèle pour qui itère à travers la liste. Si le nombre actuel est 0, j'ajoute une valeur à une nouvelle liste. Une fois la boucle Parallel for terminée, je génère la taille de la liste. La taille devrait toujours être 1000. La plupart du temps, la bonne réponse est donnée. Cependant, je l'ai vu 3 résultats incorrects possibles se produisent:

  1. La taille de la liste est inférieure à 1 000
  2. Un IndexOutOfRangeException se produit @doubleList.Add(0.0);
  3. Un ArgumentException se produit @doubleList.Add(0.0);

Le message pour le ArgumentException donné était: Destination array was not long enough. Check destIndex and length, and the array's lower bounds.

Ce qui pourrait être à l'origine de l'erreur s? Est-ce un bug .Net? Y a-t-il quelque chose que je puisse faire pour empêcher cela de se produire?

Veuillez essayer le code pour vous-même. Si vous n'obtenez pas d'erreur, essayez-le plusieurs fois. Veuillez également noter que vous ne verrez probablement aucune erreur en utilisant une machine monocœur.

using System; 
using System.Collections.Generic; 
using System.Threading.Tasks; 

namespace ParallelTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      List<int> intList = new List<int>(); 
      List<double> doubleList = new List<double>(); 

      for (int i = 0; i < 250; i++) 
      { 
       intList.Clear(); 
       doubleList.Clear(); 

       for (int j = 0; j < 10000; j++) 
       { 
        intList.Add(j % 10); 
       } 

       Parallel.For(0, intList.Count, j => 
       { 
        if (intList[j] == 0) 
        { 
         doubleList.Add(0.0); 
        } 
       }); 

       if (doubleList.Count != 1000) 
       { 
        Console.WriteLine("On iteration " + i + ": List size = " + doubleList.Count); 
       } 
      } 

      Console.WriteLine("\nPress any key to exit."); 
      Console.ReadKey(); 
     } 
    } 
} 
+1

quoi de plus probable un bug .NET ou un bug dans votre code? –

+0

Règle n ° 1 du débogage: Le bug est toujours dans votre code. Vous pouvez dire ce qui ne va pas juste à partir du * titre * de cette ... – Aaronaught

Répondre

18

Je pense que System.Collections.Generic.List n'est pas thread-safe, ce qui signifie que si vous essayez de simultanément Add de deux fils différents, les choses vont mal tourner. Ah oui, il le dit dans le docs.

Vous pouvez empêcher que cela se produise dans plusieurs façons:

  • utilisent un type de collection qui permet threadsafe ajoute (il y a quelques new ones en .Net 4.0) Verrouillage
  • avant d'ajouter
  • utiliser pour les collections de stockage local des threads, et les fusionner à la fin
  • ...

Ces sont des problèmes très typiques que vous rencontrez avec le code parallèle de données.

+1

+1. System.Collections.Generic.List n'est pas thread-safe. Il existe des collections thread-safe dans .NET Framework 4.0 –

+0

+1 pour votre troisième suggestion qui pourrait être beaucoup plus rapide que les deux autres. – tster

+0

@tster - oui, il y a même une surcharge Parallel.For pour aider avec cette stratégie: http://msdn.microsoft.com/en-us/library/dd783586.aspx – Brian

Questions connexes