Ogni volta che è necessario eseguire un'azione su un server remoto, il programma genera la richiesta, la invia, quindi attende una risposta. Userò SaveChanges()
e SaveChangesAsync()
come esempio, ma lo stesso vale per Find()
e FindAsync()
.
Supponi di avere un elenco myList
di oltre 100 elementi che devi aggiungere al tuo database. Per inserirlo, la tua funzione sarebbe simile a questa:
using(var context = new MyEDM())
{
context.MyTable.AddRange(myList);
context.SaveChanges();
}
Per prima cosa crei un'istanza di MyEDM
, aggiungi l'elenco myList
alla tabella MyTable
, quindi chiama SaveChanges()
per rendere persistenti le modifiche al database. Funziona come vuoi, i record vengono salvati, ma il tuo programma non può fare nient'altro fino al termine del commit. Questo può richiedere molto tempo a seconda di ciò che stai impegnando. Se stai effettuando il commit delle modifiche ai record, l'entità deve eseguire il commit di quelle una alla volta (una volta ho avuto un salvataggio che richiedeva 2 minuti per gli aggiornamenti)!
Per risolvere questo problema, potresti fare una delle due cose. Il primo è che puoi avviare un nuovo thread per gestire l'inserto. Mentre questo libererà il thread chiamante per continuare l'esecuzione, hai creato un nuovo thread che rimarrà lì e aspetta. Non c'è bisogno di quel sovraccarico, e questo è ciò che lo async await
schema risolve.
Per le operazioni di I / O, await
diventa rapidamente il tuo migliore amico. Prendendo la sezione del codice dall'alto, possiamo modificarla in modo che sia:
using(var context = new MyEDM())
{
Console.WriteLine("Save Starting");
context.MyTable.AddRange(myList);
await context.SaveChangesAsync();
Console.WriteLine("Save Complete");
}
È un cambiamento molto piccolo, ma ci sono effetti profondi sull'efficienza e sulle prestazioni del codice. Allora cosa succede? L'inizio del codice è lo stesso, crei un'istanza di MyEDM
e aggiungi il tuo myList
a MyTable
. Ma quando si chiama await context.SaveChangesAsync()
, l'esecuzione del codice ritorna alla funzione chiamante! Quindi, mentre aspetti che tutti quei record vengano salvati, il tuo codice può continuare a essere eseguito. Supponiamo che la funzione che conteneva il codice precedente avesse la firma di public async Task SaveRecords(List<MyTable> saveList)
, la funzione chiamante potrebbe essere simile a questa:
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");
}
Perché avresti una funzione come questa, non lo so, ma ciò che produce mostra come async await
funziona. Per prima cosa esaminiamo cosa succede.
L'esecuzione entra MyCallingFunction
, Function Starting
quindi Save Starting
viene scritta sulla console, quindi la funzione SaveChangesAsync()
viene chiamata. A questo punto, l'esecuzione ritorna MyCallingFunction
ed entra nel ciclo for scrivendo 'Continuing to Execute' fino a 1000 volte. Al SaveChangesAsync()
termine, l'esecuzione ritorna alla SaveRecords
funzione, scrivendo Save Complete
sulla console. Una volta che tutto è stato SaveRecords
completato, l'esecuzione continuerà nel modo MyCallingFunction
corretto dove era al SaveChangesAsync()
termine. Confuso? Ecco un esempio di output:
Avvio della funzione
Salva avvio
Continuando a eseguire!
Continuando a eseguire!
Continuando a eseguire!
Continuando a eseguire!
Continuando a eseguire!
....
Continuando a eseguire!
Salva completato!
Continuando a eseguire!
Continuando a eseguire!
Continuando a eseguire!
....
Continuando a eseguire!
Funzione completa!
O forse:
Avvio della funzione
Salva avvio
Continuando a eseguire!
Continuando a eseguire!
Salva completato!
Continuando a eseguire!
Continuando a eseguire!
Continuando a eseguire!
....
Continuando a eseguire!
Funzione completa!
Questa è la bellezza di async await
, il tuo codice può continuare a funzionare mentre aspetti che qualcosa finisca. In realtà, avresti una funzione più simile a questa come funzione chiamante:
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());
}
Qui sono disponibili quattro diverse funzioni di salvataggio dei record contemporaneamente . MyCallingFunction
si completerà molto più velocemente usando async await
che se le singole SaveRecords
funzioni fossero chiamate in serie.
L'unica cosa che non ho ancora toccato è la await
parola chiave. Ciò che fa è interrompere l'esecuzione della funzione corrente fino al completamento di tutto ciò Task
che stai aspettando. Quindi, nel caso dell'originale MyCallingFunction
, la riga Function Complete
non verrà scritta sulla console fino al SaveRecords
termine della funzione.
Per async await
farla breve, se hai un'opzione da usare , dovresti in quanto aumenterà notevolmente le prestazioni della tua applicazione.