Estou usando um cliente de API que é completamente assíncrono, ou seja, cada operação retorna Task
ou Task<T>
, por exemplo:
static async Task DoSomething(int siteId, int postId, IBlogClient client)
{
await client.DeletePost(siteId, postId); // call API client
Console.WriteLine("Deleted post {0}.", siteId);
}
Usando os operadores assíncronos / esperados C # 5, qual é a maneira correta / mais eficiente de iniciar várias tarefas e aguardar a conclusão de todas:
int[] ids = new[] { 1, 2, 3, 4, 5 };
Parallel.ForEach(ids, i => DoSomething(1, i, blogClient).Wait());
ou:
int[] ids = new[] { 1, 2, 3, 4, 5 };
Task.WaitAll(ids.Select(i => DoSomething(1, i, blogClient)).ToArray());
Como o cliente da API está usando o HttpClient internamente, eu esperaria que isso emitisse 5 solicitações HTTP imediatamente, gravando no console à medida que cada uma delas fosse concluída.
c#
.net
task-parallel-library
async-await
c#-5.0
Ben Foster
fonte
fonte
Respostas:
Embora você execute as operações em paralelo com o código acima, esse código bloqueia cada thread em que cada operação é executada. Por exemplo, se a chamada de rede demorar 2 segundos, cada encadeamento será interrompido por 2 segundos sem fazer nada além de esperar.
Por outro lado, o código acima
WaitAll
também bloqueia os threads e seus threads não estarão livres para processar qualquer outro trabalho até que a operação termine.Abordagem recomendada
Eu preferiria
WhenAll
que executasse suas operações de forma assíncrona em paralelo.Para fazer backup, aqui está uma postagem detalhada do blog, abordando todas as alternativas e suas vantagens / desvantagens: Como e onde a E / S assíncrona simultânea com a API da Web do ASP.NET
fonte
WaitAll
também bloqueia os threads" - não bloqueia apenas um thread, o que chamouWaitAll
?Fiquei curioso para ver os resultados dos métodos fornecidos na pergunta, bem como a resposta aceita, por isso testei.
Aqui está o código:
E a saída resultante:
fonte
Como a API que você está chamando é assíncrona, a
Parallel.ForEach
versão não faz muito sentido. Você não deve usar.Wait
aWaitAll
versão, pois isso perderia o paralelismo. Outra alternativa se o chamador for assíncrono eTask.WhenAll
depois de fazerSelect
eToArray
gerar o conjunto de tarefas. Uma segunda alternativa é usar o Rx 2.0fonte
Você pode usar a
Task.WhenAll
função que você pode passar n tarefas;Task.WhenAll
retornará uma tarefa que será executada quando todas as tarefas que você passou paraTask.WhenAll
concluir. Você precisa aguardar assincronamenteTask.WhenAll
para não bloquear o thread da interface do usuário:fonte
Parallel.ForEach
requer uma lista de trabalhadores definidos pelo usuário e um não-assíncronoAction
para executar com cada trabalhador.Task.WaitAll
eTask.WhenAll
exigem aList<Task>
, que são por definição assíncronos.Achei a resposta do RiaanDP muito útil para entender a diferença, mas ela precisa de uma correção . Não há reputação suficiente para responder ao seu comentário, portanto, minha própria resposta.
Parallel.ForEach
A saída resultante está abaixo. Os tempos de execução são comparáveis. Fiz esse teste enquanto meu computador fazia a verificação antivírus semanal. Alterar a ordem dos testes alterou os tempos de execução neles.
fonte