Solucionado (ver solução)
Solucionado
(ver solução)
5
respostas

TaskAwait qual é o melhor modo de usar ?

Olá pessoal,

Eu tenho o seguinte código, estou estudando asyncAwait aqui pelo Alura, porem não entendi muito bem como utilizar.

List<ProdutoVenda> pvs = _context.ProdutosVendas
                            .Include(pv => pv.Produto).ThenInclude(p => p.Corner)
                            .Include(pv => pv.Produto).ThenInclude(p => p.Grupo)
                            .Include(pv => pv.Venda)
                            .Where(pv => model.Corners.Contains(pv.Produto.Corner.NomeCorner) &&
                                            model.Grupos.Contains(pv.Produto.Grupo.NomeGrupo) &&
                                            Convert.ToDateTime(model.DataInicio) <= Convert.ToDateTime(pv.DataVenda) &&
                                            Convert.ToDateTime(model.DataTermino) >= Convert.ToDateTime(pv.DataVenda))
                                            .ToList();

Qual seria a melhor forma de otimizar essa query ? tenho esses dois modos

Esse:

List<ProdutoVenda> pvs = await getPVSAsync(model, _context);
/...

private async Task<List<ProdutoVenda>> getPVSAsync(RelVendaProdutosViewModel model, Context _context)
        {
            return await (_context.ProdutosVendas
                            .Include(pv => pv.Produto).ThenInclude(p => p.Corner)
                            .Include(pv => pv.Produto).ThenInclude(p => p.Grupo)
                            .Include(pv => pv.Venda)
                            .Where(pv => model.Corners.Contains(pv.Produto.Corner.NomeCorner) &&
                                            model.Grupos.Contains(pv.Produto.Grupo.NomeGrupo) &&
                                            Convert.ToDateTime(model.DataInicio) <= Convert.ToDateTime(pv.DataVenda) &&
                                            Convert.ToDateTime(model.DataTermino) >= Convert.ToDateTime(pv.DataVenda)))
                                            .ToListAsync();
        }

E esse:

IList<ProdutoVenda> pvs = await getPVSAsync(model, _context);
//...

private async Task<ProdutoVenda[]> getPVSAsync(RelVendaProdutosViewModel model, Context _context)
        {
            var listaPVS = (from pv in _context.ProdutosVendas
                                     .Include(pv => pv.Produto).ThenInclude(p => p.Corner)
                                     .Include(pv => pv.Produto).ThenInclude(p => p.Grupo)
                                     .Include(pv => pv.Venda)
                          where model.Corners.Contains(pv.Produto.Corner.NomeCorner) &&
                                             model.Grupos.Contains(pv.Produto.Grupo.NomeGrupo) &&
                                             Convert.ToDateTime(model.DataInicio) <= Convert.ToDateTime(pv.DataVenda) &&
                                             Convert.ToDateTime(model.DataTermino) >= Convert.ToDateTime(pv.DataVenda)
                          select Task.Factory.StartNew(() => pv));

            return await Task.WhenAll(listaPVS);
        }

Porém eu não sei se qualquer um dos dois estão certos, como eu deveria fazer ?

Obrigado a todos.

5 respostas

Olá, João. Tudo bom?

O método ToListAsync cria uma Lista à partir de uma Query de forma assíncrona!

A expressão select Task.Factory.StartNew(() => pv) mapeia cada PV, de forma síncrona, para uma Task que não realiza nenhum processamento, apenas retorna a própria instância de PV.

Ainda no segundo exemplo, quando você executa a linha await Task.WhenAll(listaPVS);, você está aguardando a execução de todas estas Tasks. Cada execução causará uma troca de contexto e, logo em seguida, a sincronização de contexto com a thread da interface gráfica. Com esta abordagem, estamos ferindo a performance da aplicação ao invés de a aumentar.

Para adicionar, de fato, assincronicidade ao seu código, você deve utilizar o método ToListAsync - a segunda alternativa somente gera mais carga de trabalho e fere a performance, de fato.

Solucionei sua dúvida? Sobre a expressão select Task.Factory.StartNew(() => pv), ficou mais claro como ela não traz benefícios para sua aplicação?

Abs :)

Olá Guilherme,

Então o método ToListAsync traria o beneficio de um processamento melhor ?

Mas então quando eu devo usar esse código:

Select(pv => {
    Task.Factory.StartNew(() => //...)
});

return await Task.WhenAll(listaPVS);

Você poderia me dar um outro exemplo simples, sem ser aquele da aula por favor ? (pode ser bem simples mesmo, só pra mim entender).

Obrigado,

Até mais.

João, eu gostaria de corrigir o que disse na mensagem anterior:

Ainda no segundo exemplo, quando você executa a linha await Task.WhenAll(listaPVS);, você está aguardando a execução de todas estas Tasks. Cada execução causará uma troca de contexto e, logo em seguida, a sincronização de contexto com a thread da interface gráfica. Com esta abordagem, estamos ferindo a performance da aplicação ao invés de a aumentar.

O trecho em destaque não é verdadeiro! A lista listaPVS é uma coleção de Tasks em execução, de forma paralela! Cada execução não causará uma troca de contexto, apenas a linha com o operador await causa uma troca de contexto.

Acredito que meu equívoco tenha causado um pouco de confusão. Mas, o restante é verdadeiro: as Tasks criadas não fazem nada além de retornar um objeto já instanciado na thread principal - ou seja, estamos onerando nosso sistema sem nenhum benefício em troca.

O método ToListAsync é mais adequado, porque desta forma não congelamos a interface gráfica do nosso aplicativo.

Sobre o código

Select(pv => {
    Task.Factory.StartNew(() => //...)
});

return await Task.WhenAll(listaPVS);

Ele é importante quando você tem uma coleção de itens brutos e precisa processar todos estes itens de forma independente!

Trago como exemplo o download de vários arquivos simultaneamente ou ainda quando temos vários arquivos para serem comprimidos (como ZIP ou RAR) simultaneamente!

Um código deste meu último exemplo seria escrito como:

var listaTasksCompressao = arquivos.Select(arquivo => {
    Task.Factory.StartNew(() => compactarParaZip(arquivo))
});

return await Task.WhenAll(listaTasksCompressao);

Entendo,

Então no caso seria útil para tarefas que eu precise executar varias vezes um mesmo processo, assim demandando mais tempo do que o normal, ai então eu usaria a task para executar cada arquivo em paralelo, reduzindo o custo do tempo.

No meu caso ali eu estava apenas puxando uma lista de itens, e ao transformar ela numa task eu estava piorando o tempo de processamento, por isso não tem sentido, correto ?

Mais uma ultima pergunta o metodo ToListAsync() ele faz com que a CPU faça a utilização de todos os cores disponíveis, ou apenas executa em uma thread diferente da GUI ?

solução!

Quando acessamos um banco de dados, a responsabilidade de resolver, executar e retornar é do servidor de banco de dados.

A aplicação apenas aguarda enquanto o banco faz o processamento.

O ToListAsync() cria apenas 1 Task para aguardar o servidor responder (e o TaskScheduler usa apenas uma Thread para esta Task), porque não há trabalho a ser feito enquanto o servidor resolve a query.