comportamento async await [duplicado]
Eu estava experimentando o async-await e me deparei com um comportamento bastante estranho, pelo menos para mim.
Criei três métodos que simulavam tarefas de longa execução.
Considere os dois manipuladores de clique de botão:
Para button1_click, o tempo decorrido foi em torno de 6.000 ms enquanto button2_click em torno de 3.000 ms.
Não consigo entender por que isso aconteceu, ou seja, 6000ms vs 3000ms.
private async Task<string> TaskOne()
{
await Task.Delay(1000);
return "task one";
}
private async Task<string> TaskTwo()
{
await Task.Delay(2000);
return "task two";
}
private async Task<string> TaskThree()
{
await Task.Delay(3000);
return "task three";
}
//time elapsed = 6000+ms
private async void button1_Click(object sender, EventArgs e)
{
var watch = new Stopwatch();
watch.Start();
await TaskOne();
await TaskTwo();
await TaskThree();
watch.Stop();
textBox3.Text = watch.ElapsedMilliseconds.ToString();
}
//time elapsed = 3000+ms
private async void button2_Click(object sender, EventArgs e)
{
var watch = new Stopwatch();
watch.Start();
var taskOne = TaskOne();
var taskTwo = TaskTwo();
var taskThree = TaskThree();
await taskOne;
await taskTwo;
await taskThree;
watch.Stop();
textBox3.Text = watch.ElapsedMilliseconds.ToString();
}
Respostas
Nesse caso:
await TaskOne();
await TaskTwo();
await TaskThree();
TaskTwo () não pode ser iniciado até que TaskOne () seja concluído porque você está esperando por ele. Da mesma forma, TaskThree () não pode iniciar até que TaskTwo () seja concluída por causa do await.
Na próxima:
var taskOne = TaskOne();
var taskTwo = TaskTwo();
var taskThree = TaskThree();
await taskOne;
await taskTwo;
await taskThree;
Você está iniciando todas as 3 tarefas ao mesmo tempo e depois aguardando por elas. É por isso que está demorando apenas o tempo da tarefa de execução mais longa. Você ficaria surpreso com quantas pessoas não entendem isso sobre espera assíncrona. Se as tarefas não dependerem umas das outras, este é o caminho a percorrer.
Resumo
O ponto principal aqui (e é um equívoco muito comum de outra forma), é que awaitrealmente significa "aguardar" .
operador de espera (referência C #)
Ênfase minha
O operador await suspende a avaliação do método assíncrono envolvente até que a operação assíncrona representada por seu operando seja concluída . Quando a operação assíncrona é concluída, o operador await retorna o resultado da operação, se houver.
Quando o operador await é aplicado ao operando que representa uma operação já concluída, ele retorna o resultado da operação imediatamente, sem suspensão do método de fechamento .
O operador await não bloqueia o thread que avalia o método assíncrono. Quando o operador await suspende o método assíncrono envolvente, o controle retorna ao chamador do método.
Então aqui está o que está acontecendo. Em seu primeiro exemplo, você está iniciando cada tarefa e esperando que sejam concluídas consecutivamente. Quer dizer, é como pedir a alguém para ir fazer algo e terminar, antes de pedir a outra pessoa para fazer algo, etc.
await TaskOne(); // start, do something and wait for it
await TaskTwo(); // start, do something and wait for it
await TaskThree(); // start, do something and wait for it
Seu segundo exemplo. Você está essencialmente iniciando as 3 tarefas (quentes) e, em seguida, esperando que concluam uma de cada vez. Isso quer dizer que eles são executados simultaneamente e aguardados em sequência.
Ou seja, você está dizendo a 3 amigos, vá fazer as coisas, depois espere o primeiro voltar, depois o segundo e o terceiro. É muito mais eficiente ... Nenhum amigo chato por perto até que o anterior volte.
Mesmo se a segunda tarefa for concluída antes da primeira , você ainda estará esperando pela primeira tarefa antes de olhar para o estado concluído da segunda tarefa, etc.
var taskOne = TaskOne(); // start, do something
var taskTwo = TaskTwo(); // start, do something
var taskThree = TaskThree(); // start, do something
// all 3 tasks are started
await taskOne; // wait for it
await taskTwo; // wait for it
await taskThree; // wait for it
Ou da mesma forma, você poderia usar Task.WhenAll
Cria uma tarefa que será concluída quando todas as tarefas fornecidas forem concluídas.
var taskOne = TaskOne(); // start, do something
var taskTwo = TaskTwo(); // start, do something
var taskThree = TaskThree(); // start, do something
// wait for them all to finish!
await Task.WhenAll(taskOne, taskTwo, taskThree);