2011-02-23 21 views
8

Tengo un método que devuelve IEnumerable<string> que, por supuesto, se está manejando con yield return <string>;. Quiero tener múltiples hilos procesando el resultado de esto, por supuesto, sin repetirlo y sin hilos. ¿Cómo lograría esto?¿Cómo hacer que varios hilos procesen el mismo resultado de IEnumerable?

var result = GetFiles(source); 

for (int i = 0; i < Environment.ProcessorCount; i++) 
{ 
    tasks.Add(Task.Factory.StartNew(() => { ProcessCopy(result); })); 
} 

Task.WaitAll(tasks.ToArray()); 

Sin embargo esto parece ser la producción de repeticiones:

C:\Users\esac\Pictures\2000-06\DSC_1834.JPG 
C:\Users\esac\Pictures\2000-06\DSC_1835.JPG 
C:\Users\esac\Pictures\2000-06\.picasa.ini 
C:\Users\esac\Pictures\2000-06\DSC_1834.JPG 
C:\Users\esac\Pictures\2000-06\DSC_1835.JPG 
C:\Users\esac\Pictures\2000-06\.picasa.ini 
C:\Users\esac\Pictures\2000-06\DSC_1834.JPG 
C:\Users\esac\Pictures\2000-06\DSC_1835.JPG 
C:\Users\esac\Pictures\2000-06\.picasa.ini 
C:\Users\esac\Pictures\2000-06\DSC_1834.JPG 
C:\Users\esac\Pictures\2000-06\DSC_1835.JPG 

Respuesta

9

Puede hacerlo fácilmente utilizando el método Parallel.ForEach.

Write a Simple Parallel.ForEach loop

Cada iteración se pondrán en cola en el administrador de tareas. El ciclo saldrá cuando se realicen todas las iteraciones.

var result = GetFiles(source); 

Parallel.ForEach(result, current => { 
    ProcessCopy(current); 
}); 

Console.WriteLine("Done"); 
+0

Bien, eso funcionó para mí. – esac

+0

+1 para la mejor solución del problema ;-) – BrokenGlass

4

Tienes que escoger una serie de elementos para cada ProcessCopy() llamada - en este momento que está pasando cada hilo de la enumeración completa de los archivos - recordar que el IEnumerable que está aprobando tiene un método llamado GetEnumerator() - solo cuando se llama a ese método (que foreach hace por usted bajo el capó) se devuelve el Enumerador real, con el cual puede enumerar los elementos uno por uno. Como está pasando el IEnumerable, cada hilo llama al GetEnumerator() y, por lo tanto, enumera todos los archivos.

En vez de hacer algo como esto para tener cada ProcessCopy() proceso de un solo archivo:

foreach(string file in GetFiles(source)) 
{ 
    string fileToProcess = file; 
    tasks.Add(Task.Factory.StartNew(() => { ProcessCopy(fileToProcess); })); 
} 

Task.WaitAll(tasks.ToArray()); 

yo no me preocuparía por número de procesadores - dejar que el TPL y el grupo de subprocesos calcular la cantidad de hilos para funcionar para un rendimiento óptimo .

+0

+1 para invocar el error original. – Josh

1

¿Por qué no utilizar una consulta LINQ simple para hacer lo que quiera?

var tasks = 
    from f in GetFiles(source) 
    select Task.Factory.StartNew(() => { ProcessCopy(f); }); 

Task.WaitAll(tasks.ToArray()); 

Detrás de las escenas TPL maneja todo el repulsivo Environment.ProcessorCount cosas para usted de todos modos.

+0

Esencialmente lo mismo que BrokenGlass pero con LINQ que me gusta. El Parallel.ForEach parece más limpio hasta ahora, pero esta es definitivamente la forma en que debería haber pensado sobre el problema. – esac

Cuestiones relacionadas