Parallel.ForEach vs Task.Run y ​​Task.WhenAll

¿Cuáles son las diferencias entre utilizar Parallel.ForEach o Task.Run () para iniciar un conjunto de tareas de forma asincrónica?

Versión 1:

List strings = new List { "s1", "s2", "s3" }; Parallel.ForEach(strings, s => { DoSomething(s); }); 

Versión 2:

 List strings = new List { "s1", "s2", "s3" }; List Tasks = new List(); foreach (var s in strings) { Tasks.Add(Task.Run(() => DoSomething(s))); } await Task.WhenAll(Tasks); 

En este caso, el segundo método esperará asíncronamente a que se completen las tareas en lugar de bloquear.

Sin embargo, existe la desventaja de utilizar Task.Run en un bucle: con Parallel.ForEach , hay un Partitioner que se crea para evitar hacer más tareas de las necesarias. Task.Run siempre realizará una única tarea por elemento (ya que lo está haciendo), pero los lotes de la clase Parallel funcionan, por lo que crea menos tareas que los elementos de trabajo totales. Esto puede proporcionar un rendimiento general significativamente mejor, especialmente si el cuerpo del bucle tiene una pequeña cantidad de trabajo por artículo.

Si este es el caso, puede combinar ambas opciones escribiendo:

 await Task.Run(() => Parallel.ForEach(strings, s => { DoSomething(s); })); 

Tenga en cuenta que esto también se puede escribir en esta forma más corta:

 await Task.Run(() => Parallel.ForEach(strings, DoSomething)); 

La primera versión bloqueará sincrónicamente el hilo de llamada (y ejecutará algunas de las tareas en él).
Si es un hilo de IU, esto congelará la IU.

La segunda versión ejecutará las tareas de forma asíncrona en el grupo de subprocesos y liberará el subproceso de llamada hasta que finalice.

También hay diferencias en los algoritmos de progtwigción utilizados.

Tenga en cuenta que su segundo ejemplo puede acortarse a

 await Task.WhenAll(strings.Select(s => Task.Run(() => DoSomething(s))); 

Terminé haciendo esto, ya que me pareció más fácil de leer:

  List x = new List(); foreach(var s in myCollectionOfObject) { // Note there is no await here. Just collection the Tasks x.Add(s.DoSomethingAsync()); } await Task.WhenAll(x);