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 myListdi 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 myListalla 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 awaitschema risolve.
Per le operazioni di I / O, awaitdiventa 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 MyEDMe aggiungi il tuo myLista 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 awaitfunziona. Per prima cosa esaminiamo cosa succede.
L'esecuzione entra MyCallingFunction, Function Startingquindi Save Startingviene scritta sulla console, quindi la funzione SaveChangesAsync()viene chiamata. A questo punto, l'esecuzione ritorna MyCallingFunctioned entra nel ciclo for scrivendo 'Continuing to Execute' fino a 1000 volte. Al SaveChangesAsync()termine, l'esecuzione ritorna alla SaveRecordsfunzione, scrivendo Save Completesulla console. Una volta che tutto è stato SaveRecordscompletato, l'esecuzione continuerà nel modo MyCallingFunctioncorretto 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 . MyCallingFunctionsi completerà molto più velocemente usando async awaitche se le singole SaveRecordsfunzioni fossero chiamate in serie.
L'unica cosa che non ho ancora toccato è la awaitparola chiave. Ciò che fa è interrompere l'esecuzione della funzione corrente fino al completamento di tutto ciò Taskche stai aspettando. Quindi, nel caso dell'originale MyCallingFunction, la riga Function Completenon verrà scritta sulla console fino al SaveRecordstermine della funzione.
Per async awaitfarla breve, se hai un'opzione da usare , dovresti in quanto aumenterà notevolmente le prestazioni della tua applicazione.