2010-05-02 11 views
6

Tengo algunos problemas relacionados con Parallel for loops y agregarlos a una lista. El problema es que el mismo código puede generar resultados diferentes en diferentes momentos. He configurado algunos códigos de prueba a continuación. En este código, creo una lista de 10,000 valores int. 1/10 de los valores será 0, 1/10 de los valores será 1, hasta 1/10 de los valores siendo 9.Parallel For Loop - Problemas al agregar a una lista

Después de configurar esta lista, configuro un paralelo para el ciclo que itera a través de la lista. Si el número actual es 0, agrego un valor a una nueva lista. Una vez que se completa el bucle Parallel for, obtengo el tamaño de la lista. El tamaño siempre debe ser 1,000. La mayoría de las veces, se da la respuesta correcta. Sin embargo, he visto 3 posibles resultados incorrectos se producen:

  1. El tamaño de la lista es de menos de 1000
  2. Un IndexOutOfRangeException ocurre @doubleList.Add(0.0);
  3. Un ArgumentException ocurre @doubleList.Add(0.0);

El mensaje para el ArgumentException dado fue: Destination array was not long enough. Check destIndex and length, and the array's lower bounds.

¿Qué podría estar causando el error s? ¿Es esto un error de .Net? ¿Hay algo que pueda hacer para evitar que esto suceda?

Pruebe el código usted mismo. Si no obtiene un error, inténtelo algunas veces. Tenga en cuenta que probablemente no verá ningún error al usar una máquina de un solo núcleo.

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

¿qué es más probable un error de .NET o un error en SU ​​código? –

+0

Regla # 1 de depuración: el error está siempre en su código. Puede decir lo que está mal solo por el * título * de esto ... – Aaronaught

Respuesta

18

espero que System.Collections.Generic.List no es seguro para subprocesos, lo que significa que si se intenta simultáneamente Add a partir de dos hilos diferentes, las cosas van a salir mal. Ah sí, lo dice en el docs.

Se podría evitar que esto suceda en un número de maneras:

  • uso de un tipo de colección que permite multi-hilo añade (hay algunos new ones en .Net 4.0)
  • de bloqueo antes de añadir
  • utilizar el almacenamiento local de subprocesos para las colecciones, y unirlos al final
  • ...

Estos son problemas muy típicos con los que se encuentra el código paralelo de datos.

+1

+1. System.Collections.Generic.List no es seguro para subprocesos. Hay colecciones de subprocesos seguros en .NET Framework 4.0 –

+0

+1 para su tercera sugerencia, que podría ser mucho más rápido que los otros dos. – tster

+0

@tster - sí, incluso hay un Paralelo. Para sobrecargar para ayudar con esa estrategia: http://msdn.microsoft.com/en-us/library/dd783586.aspx – Brian