Quais são as diferenças entre usar Parallel.ForEach ou Task.Run () para iniciar um conjunto de tarefas de forma assíncrona?
Versão 1:
List<string> strings = new List<string> { "s1", "s2", "s3" };
Parallel.ForEach(strings, s =>
{
DoSomething(s);
});
Versão 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
fonte
fonte
Task.WaitAll
vez deTask.WhenAll
.Respostas:
Nesse caso, o segundo método aguardará assincronamente a conclusão das tarefas, em vez de o bloquear.
No entanto, há uma desvantagem a ser usada
Task.Run
em um loop - ComParallel.ForEach
, existe umaPartitioner
que é criada para evitar a realização de mais tarefas do que o necessário.Task.Run
sempre criará uma única tarefa por item (já que você está fazendo isso), mas osParallel
lotes da classe funcionam para que você crie menos tarefas que o total de itens de trabalho. Isso pode proporcionar um desempenho geral significativamente melhor, especialmente se o corpo do loop tiver uma pequena quantidade de trabalho por item.Se for esse o caso, você pode combinar as duas opções escrevendo:
Observe que isso também pode ser escrito neste formato mais curto:
fonte
DoSomething
forasync void DoSomething
?async Task DoSomething
?A primeira versão bloqueará de forma síncrona o segmento de chamada (e executará algumas das tarefas nele).
Se for um thread da interface do usuário, isso congelará a interface do usuário.
A segunda versão executará as tarefas de forma assíncrona no pool de threads e liberará o thread de chamada até que elas sejam concluídas.
Também existem diferenças nos algoritmos de agendamento utilizados.
Observe que seu segundo exemplo pode ser reduzido para
fonte
await Task.WhenAll(strings.Select(async s => await Task.Run(() => DoSomething(s)));
? Eu tive problemas ao retornar tarefas (em vez de esperar), especialmente quando declarações comousing
estavam envolvidas para descartar objetos.Acabei fazendo isso, pois parecia mais fácil ler:
fonte
Eu vi Parallel.ForEach usado de forma inadequada e achei que um exemplo nesta pergunta ajudaria.
Ao executar o código abaixo em um aplicativo de console, você verá como as tarefas executadas no Parallel.ForEach não bloqueiam o segmento de chamada. Isso pode ser bom se você não se importar com o resultado (positivo ou negativo), mas se precisar do resultado, certifique-se de usar o Task.WhenAll.
Aqui está o resultado:
Conclusão:
Usar o Parallel.ForEach com uma tarefa não bloqueará o thread de chamada. Se você se importa com o resultado, aguarde as tarefas.
~ Felicidades
fonte