Eu tenho poucos serviços REST assíncronos que não são dependentes um do outro. Ou seja, enquanto "aguarda" uma resposta do Service1, posso chamar Service2, Service3 e assim por diante.
Por exemplo, consulte o código abaixo:
var service1Response = await HttpService1Async();
var service2Response = await HttpService2Async();
// Use service1Response and service2Response
Agora, service2Response
não depende service1Response
e eles podem ser buscados independentemente. Portanto, não há necessidade de aguardar a resposta do primeiro serviço para chamar o segundo serviço.
Eu não acho que posso usar Parallel.ForEach
aqui, pois não é uma operação vinculada à CPU.
Para chamar essas duas operações em paralelo, posso chamar use Task.WhenAll
? Um problema que vejo usando Task.WhenAll
é que ele não retorna resultados. Para buscar o resultado, posso ligar task.Result
depois de ligar Task.WhenAll
, pois todas as tarefas já estão concluídas e tudo o que preciso para nos buscar uma resposta?
Código de amostra:
var task1 = HttpService1Async();
var task2 = HttpService2Async();
await Task.WhenAll(task1, task2)
var result1 = task1.Result;
var result2 = task2.Result;
// Use result1 and result2
Esse código é melhor que o primeiro em termos de desempenho? Alguma outra abordagem que eu possa usar?
fonte
I do not think I can use Parallel.ForEach here since it is not CPU bound operation
- Eu não vejo a lógica lá. Simultaneidade é simultaneidade.Parallel.ForEach
geraria novos threads, enquantoasync await
faria tudo em um único thread.await
) antes de estar pronta.WhenAll
antes deResult
concluir a ideia de que ele conclui todas as tarefas antes de .Result ser chamado. Como o Task.Result bloqueia o segmento de chamada, presumo que, se eu chamá-lo após a conclusão das tarefas, retornará o resultado imediatamente. Eu quero validar o entendimento.Respostas:
Mas faz retornar os resultados. Todos eles estarão em uma matriz de um tipo comum; portanto, nem sempre é útil usar os resultados, pois você precisa encontrar o item na matriz que corresponde ao valor para o
Task
qual deseja o resultado e potencialmente convertê-lo em sua tipo real, portanto, pode não ser a abordagem mais fácil / mais legível nesse contexto, mas quando você deseja obter todos os resultados de cada tarefa, e o tipo comum é o tipo em que você deseja tratá-los, é ótimo .Sim, você poderia fazer isso. Você também pode
await
usá-los (await
desembrulharia a exceção em qualquer tarefa com falha, ao passoResult
que lançaria uma exceção agregada, mas, caso contrário, seria a mesma).Ele executa as duas operações ao mesmo tempo, em vez de uma e depois a outra. Se isso é melhor ou pior, depende do que são essas operações subjacentes. Se as operações subjacentes forem "ler um arquivo do disco", é provável que fazê-las em paralelo seja mais lento, pois há apenas uma cabeça de disco e ela pode estar em um local a qualquer momento; saltar entre dois arquivos será mais lento do que ler um arquivo e depois outro. Por outro lado, se as operações "executam algumas solicitações de rede" (como é o caso aqui), provavelmente serão mais rápidas (pelo menos até um certo número de solicitações simultâneas), porque você pode esperar por uma resposta de outro computador da rede com a mesma rapidez quando houver outra solicitação de rede pendente em andamento. Se você quer saber se '
Se não for importante para você conhecer todas as exceções lançadas entre todas as operações que você está executando em paralelo, em vez de apenas a primeira, você pode simplesmente
await
executar as tarefas semWhenAll
absolutamente nenhuma . A única coisa queWhenAll
você oferece é terAggregateException
todas as exceções de todas as tarefas com falha, em vez de lançar quando você atingir a primeira tarefa com falha. É tão simples quanto:fonte
Aqui está o método de extensão que utiliza o SemaphoreSlim e permite definir o grau máximo de paralelismo
Uso da amostra:
fonte
Você pode usar
ou
fonte