Sempre que você precisar realizar uma ação em um servidor remoto, seu programa gera a solicitação, a envia e espera por uma resposta. Vou usar SaveChanges()
e SaveChangesAsync()
como exemplo, mas o mesmo se aplica a Find()
e FindAsync()
.
Digamos que você tenha uma lista myList
de mais de 100 itens que precise adicionar ao seu banco de dados. Para inserir isso, sua função seria mais ou menos assim:
using(var context = new MyEDM())
{
context.MyTable.AddRange(myList);
context.SaveChanges();
}
Primeiro você cria uma instância de MyEDM
, adiciona a lista myList
à tabela e MyTable
, em seguida, chama SaveChanges()
para persistir as alterações no banco de dados. Funciona como você deseja, os registros são confirmados, mas seu programa não pode fazer mais nada até que a confirmação seja concluída. Isso pode levar muito tempo, dependendo do que você está enviando. Se você está enviando alterações para os registros, a entidade tem que confirmá-las, uma de cada vez (uma vez, um salvamento levou 2 minutos para atualizações)!
Para resolver esse problema, você pode fazer uma das duas coisas. A primeira é que você pode iniciar uma nova rosca para lidar com a inserção. Embora isso libere o encadeamento de chamada para continuar em execução, você criou um novo encadeamento que vai ficar parado e esperar. Não há necessidade dessa sobrecarga, e é isso que o async await
padrão resolve.
Para operações de I / O, await
rapidamente se torna seu melhor amigo. Pegando a seção de código acima, podemos modificá-la para ser:
using(var context = new MyEDM())
{
Console.WriteLine("Save Starting");
context.MyTable.AddRange(myList);
await context.SaveChangesAsync();
Console.WriteLine("Save Complete");
}
É uma mudança muito pequena, mas há efeitos profundos na eficiência e no desempenho do seu código. Então o que acontece? O início do código é o mesmo, você cria uma instância de MyEDM
e adiciona seu myList
a MyTable
. Mas quando você chama await context.SaveChangesAsync()
, a execução do código retorna para a função de chamada! Portanto, enquanto você espera a confirmação de todos esses registros, seu código pode continuar a ser executado. Digamos que a função que continha o código acima tivesse a assinatura de public async Task SaveRecords(List<MyTable> saveList)
, a função de chamada poderia ter a seguinte aparência:
public async Task MyCallingFunction()
{
Console.WriteLine("Function Starting");
Task saveTask = SaveRecords(GenerateNewRecords());
for(int i = 0; i < 1000; i++){
Console.WriteLine("Continuing to execute!");
}
await saveTask;
Console.Log("Function Complete");
}
Por que você teria uma função como esta, eu não sei, mas o que sai mostra como async await
funciona. Primeiro, vamos ver o que acontece.
Execução entra MyCallingFunction
, Function Starting
em seguida, Save Starting
é escrita para o console, em seguida, a função SaveChangesAsync()
é chamada. Neste ponto, a execução retorna MyCallingFunction
e entra no loop for escrevendo 'Continuing to Execute' até 1000 vezes. Ao SaveChangesAsync()
terminar, a execução retorna à SaveRecords
função, gravando Save Complete
no console. Depois que tudo estiver SaveRecords
concluído, a execução continuará da maneira MyCallingFunction
correta onde estava antes SaveChangesAsync()
. Confuso? Aqui está um exemplo de saída:
Início da função
Salvar começando
Continuando a executar!
Continuando a executar!
Continuando a executar!
Continuando a executar!
Continuando a executar!
....
Continuando a executar!
Salvar concluído!
Continuando a executar!
Continuando a executar!
Continuando a executar!
....
Continuando a executar!
Função concluída!
Ou talvez:
Início da função
Salvar começando
Continuando a executar!
Continuando a executar!
Salvar concluído!
Continuando a executar!
Continuando a executar!
Continuando a executar!
....
Continuando a executar!
Função concluída!
Essa é a beleza de async await
, seu código pode continuar a ser executado enquanto você espera que algo termine. Na realidade, você teria uma função mais parecida com esta como sua função de chamada:
public async Task MyCallingFunction()
{
List<Task> myTasks = new List<Task>();
myTasks.Add(SaveRecords(GenerateNewRecords()));
myTasks.Add(SaveRecords2(GenerateNewRecords2()));
myTasks.Add(SaveRecords3(GenerateNewRecords3()));
myTasks.Add(SaveRecords4(GenerateNewRecords4()));
await Task.WhenAll(myTasks.ToArray());
}
Aqui, você tem quatro funções diferentes de salvar gravação em execução ao mesmo tempo . MyCallingFunction
será concluído muito mais rápido usando do async await
que se as SaveRecords
funções individuais fossem chamadas em série.
A única coisa que ainda não mencionei é a await
palavra - chave. O que isso faz é parar a execução da função atual até que tudo Task
o que você está esperando seja concluído. Portanto, no caso do original MyCallingFunction
, a linha Function Complete
não será gravada no console até que a SaveRecords
função seja concluída.
Resumindo, se você tem uma opção de usar async await
, você deve, pois isso aumentará muito o desempenho do seu aplicativo.