Caso eu não me importe com a ordem de conclusão da tarefa e precise apenas de todos para concluir, ainda devo usar em await Task.WhenAll
vez de vários await
? por exemplo, está DoWork2
abaixo de um método preferido para DoWork1
(e por quê?):
using System;
using System.Threading.Tasks;
namespace ConsoleApp
{
class Program
{
static async Task<string> DoTaskAsync(string name, int timeout)
{
var start = DateTime.Now;
Console.WriteLine("Enter {0}, {1}", name, timeout);
await Task.Delay(timeout);
Console.WriteLine("Exit {0}, {1}", name, (DateTime.Now - start).TotalMilliseconds);
return name;
}
static async Task DoWork1()
{
var t1 = DoTaskAsync("t1.1", 3000);
var t2 = DoTaskAsync("t1.2", 2000);
var t3 = DoTaskAsync("t1.3", 1000);
await t1; await t2; await t3;
Console.WriteLine("DoWork1 results: {0}", String.Join(", ", t1.Result, t2.Result, t3.Result));
}
static async Task DoWork2()
{
var t1 = DoTaskAsync("t2.1", 3000);
var t2 = DoTaskAsync("t2.2", 2000);
var t3 = DoTaskAsync("t2.3", 1000);
await Task.WhenAll(t1, t2, t3);
Console.WriteLine("DoWork2 results: {0}", String.Join(", ", t1.Result, t2.Result, t3.Result));
}
static void Main(string[] args)
{
Task.WhenAll(DoWork1(), DoWork2()).Wait();
}
}
}
await t1; await t2; ....; await tn
=> o segundo é sempre a melhor escolha em ambos os casosRespostas:
Sim, use
WhenAll
porque propaga todos os erros de uma só vez. Com o múltiplo aguarda, você perde erros se um dos anteriores aguarda arremessos.Outra diferença importante é que
WhenAll
aguardará a conclusão de todas as tarefas, mesmo na presença de falhas (tarefas com falha ou canceladas). Aguardar manualmente em sequência causaria simultaneidade inesperada, porque a parte do seu programa que deseja aguardar continuará mais cedo.Eu acho que também facilita a leitura do código, porque a semântica que você deseja está diretamente documentada no código.
fonte
await
resultar.Meu entendimento é que o principal motivo para preferir
Task.WhenAll
múltiplosawait
s é desempenho / tarefa "agitação": oDoWork1
método faz algo parecido com isto:Por outro lado,
DoWork2
faz isso:Se isso é importante o suficiente para o seu caso em particular, é "dependente do contexto" (perdoe o trocadilho).
fonte
Um método assíncrono é implementado como uma máquina de estado. É possível escrever métodos para que não sejam compilados em máquinas de estado, isso geralmente é chamado de método assíncrono de via rápida. Eles podem ser implementados da seguinte maneira:
Ao usá-
Task.WhenAll
lo, é possível manter esse código acelerado, assegurando ao mesmo tempo que o chamador aguarda a conclusão de todas as tarefas, por exemplo:fonte
(Isenção de responsabilidade: Esta resposta foi tirada / inspirada do curso TPL Async de Ian Griffiths sobre Pluralsight )
Outro motivo para preferir o tratamento WhenAll é Exception.
Suponha que você tenha um bloco try-catch nos seus métodos DoWork e suponha que eles estejam chamando diferentes métodos DoTask:
Nesse caso, se todas as três tarefas gerarem exceções, apenas a primeira será capturada. Qualquer exceção posterior será perdida. Ou seja, se t2 e t3 lançarem uma exceção, apenas t2 será capturado; etc. As exceções de tarefas subsequentes não serão observadas.
Onde, como no WhenAll - se alguma ou todas as tarefas falharem, a tarefa resultante conterá todas as exceções. A palavra-chave wait sempre ainda lança novamente a primeira exceção. Portanto, as outras exceções ainda são efetivamente não observadas. Uma maneira de superar isso é adicionar uma continuação vazia após a tarefa WhenAll e colocar a espera lá. Dessa forma, se a tarefa falhar, a propriedade result lançará a Exceção Agregada completa:
fonte
As outras respostas a esta pergunta oferecem razões técnicas pelas quais
await Task.WhenAll(t1, t2, t3);
é preferido. Esta resposta terá como objetivo analisá-la de um lado mais suave (ao qual @usr faz alusão) e ainda chegar à mesma conclusão.await Task.WhenAll(t1, t2, t3);
é uma abordagem mais funcional, pois declara intenção e é atômica.Com
await t1; await t2; await t3;
, não há nada que impeça um colega de equipe (ou talvez o seu futuro eu!) De adicionar código entre asawait
declarações individuais . Claro, você o compactou em uma linha para conseguir isso, mas isso não resolve o problema. Além disso, geralmente é uma má forma em um ambiente de equipe incluir várias instruções em uma determinada linha de código, pois pode dificultar o arquivo de origem para os olhos humanos verificarem.Simplificando,
await Task.WhenAll(t1, t2, t3);
é mais sustentável, pois comunica sua intenção com mais clareza e é menos vulnerável a bugs peculiares que podem resultar de atualizações bem-intencionadas do código, ou até mesmo se fundem de maneira errada.fonte