¿Cuáles son las diferencias entre usar Parallel.ForEach o Task.Run () para iniciar un conjunto de tareas de forma asincrónica?
Versión 1:
List<string> strings = new List<string> { "s1", "s2", "s3" };
Parallel.ForEach(strings, s =>
{
DoSomething(s);
});
Versión 2:
List<string> strings = new List<string> { "s1", "s2", "s3" };
List<Task> Tasks = new List<Task>();
foreach (var s in strings)
{
Tasks.Add(Task.Run(() => DoSomething(s)));
}
await Task.WhenAll(Tasks);
c#
async-await
parallel.foreach
Petter T
fuente
fuente
Task.WaitAll
lugar deTask.WhenAll
.Respuestas:
En este caso, el segundo método esperará asincrónicamente a que se completen las tareas en lugar de bloquearlas.
Sin embargo, hay una desventaja de usar
Task.Run
en un bucleParallel.ForEach
: hay unaPartitioner
que se crea para evitar realizar más tareas de las necesarias.Task.Run
siempre realizará una única tarea por elemento (ya que está haciendo esto), pero losParallel
lotes de clase funcionan, por lo que crea menos tareas que el total de elementos de trabajo. Esto puede proporcionar un rendimiento general significativamente mejor, especialmente si el cuerpo del bucle tiene una pequeña cantidad de trabajo por elemento.Si este es el caso, puede combinar ambas opciones escribiendo:
Tenga en cuenta que esto también se puede escribir en esta forma más corta:
fuente
DoSomething
es asíasync void DoSomething
?async Task DoSomething
?La primera versión bloqueará sincrónicamente el hilo de llamada (y ejecutará algunas de las tareas en él).
Si es un subproceso de interfaz de usuario, esto congelará la interfaz de usuario.
La segunda versión ejecutará las tareas de forma asincrónica en el grupo de subprocesos y liberará el subproceso de llamada hasta que estén listas.
También hay diferencias en los algoritmos de programación utilizados.
Tenga en cuenta que su segundo ejemplo se puede acortar a
fuente
await Task.WhenAll(strings.Select(async s => await Task.Run(() => DoSomething(s)));
? Tuve problemas al devolver tareas (en lugar de esperar), especialmente cuando las declaraciones comousing
estaban involucradas para deshacerse de los objetos.Terminé haciendo esto, ya que me pareció más fácil de leer:
fuente
He visto Parallel.ForEach usado inapropiadamente, y pensé que un ejemplo en esta pregunta ayudaría.
Cuando ejecute el código a continuación en una aplicación de consola, verá cómo las tareas ejecutadas en Parallel.ForEach no bloquean el hilo de llamada. Esto podría estar bien si no le importa el resultado (positivo o negativo) pero si necesita el resultado, debe asegurarse de usar Task.WhenAll.
Aquí está el resultado:
Conclusión:
El uso de Parallel.ForEach con una tarea no bloqueará el hilo de llamada. Si te importa el resultado, asegúrate de esperar las tareas.
~ Saludos
fuente