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()
.
Supponiamo di avere una lista myList
di 100+ elementi che è necessario aggiungere al proprio database. Per inserire questo, la funzione potrebbe essere simile così:
using(var context = new MyEDM())
{
context.MyTable.AddRange(myList);
context.SaveChanges();
}
Per prima cosa è creare e istanza di MyEDM
, aggiungere l'elenco myList
al tavolo MyTable
, quindi chiamare SaveChanges()
a persistere le modifiche al database. Funziona come vuoi, i record vengono attivati, ma il tuo programma non può fare nient'altro fino a quando il commit non finisce. Questo può richiedere molto tempo a seconda di ciò che stai commettendo. Se si stanno commettendo modifiche ai record, l'entità deve impegnarli uno alla volta (una volta ho avuto un salvataggio in 2 minuti per gli aggiornamenti)!
Per risolvere questo problema, è possibile fare una delle due cose. Il primo è che puoi avviare una nuova discussione per gestire l'inserimento. Mentre questo consente di liberare il thread chiamante per continuare l'esecuzione, hai creato un nuovo thread che si sta semplicemente seduto lì e aspetta. Non è necessario per questo sovraccarico, e questo è ciò che risolve il modello async await
.
Per le operazioni di I/O, await
diventa rapidamente il vostro migliore amico.Prendendo la sezione di codice da sopra, possiamo modificare per essere:
using(var context = new MyEDM())
{
Console.WriteLine("Save Starting");
context.MyTable.AddRange(myList);
await context.SaveChangesAsync();
Console.WriteLine("Save Complete");
}
E 'un piccolo cambiamento, ma ci sono effetti profondi sulla efficienza e le prestazioni del vostro codice. Quindi cosa succede? L'inizio del codice è lo stesso, si crea un'istanza di MyEDM
e si aggiunge myList
a MyTable
. Ma quando chiami await context.SaveChangesAsync()
, l'esecuzione del codice torna alla funzione di chiamata! Quindi, mentre stai aspettando che tutti quei record vengano confermati, il tuo codice può continuare a essere eseguito. Di 'la funzione che conteneva il codice di cui sopra ha avuto la firma del public async Task SaveRecords(List<MyTable> saveList)
, funzione chiamante potrebbe assomigliare a questo:
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é si avrebbe una funzione come questa, io non lo so, ma quello che emette mostra come async await
funziona. Prima lascia andare su quello che succede.
L'esecuzione entra in MyCallingFunction
, Function Starting
quindi Save Starting
viene scritto nella console, quindi viene richiamata la funzione SaveChangesAsync()
. A questo punto, l'esecuzione ritorna a MyCallingFunction
e inserisce il ciclo for che scrive 'Continuazione per l'esecuzione' fino a 1000 volte. Al termine di SaveChangesAsync()
, l'esecuzione torna alla funzione SaveRecords
, scrivendo Save Complete
nella console. Una volta completato il tutto in SaveRecords
, l'esecuzione continuerà nel MyCallingFunction
giusto quando è stato completato lo SaveChangesAsync()
. Confuso? Ecco un esempio di output:
Function Starting
Save Starting
Continuing to execute!
Continuing to execute!
Continuing to execute!
Continuing to execute!
Continuing to execute!
....
Continuing to execute!
Save Complete!
Continuing to execute!
Continuing to execute!
Continuing to execute!
....
Continuing to execute!
Function Complete!
O forse:
Function Starting
Save Starting
Continuing to execute!
Continuing to execute!
Save Complete!
Continuing to execute!
Continuing to execute!
Continuing to execute!
....
Continuing to execute!
Function Complete!
Questa è la bellezza della async await
, il codice può continuare a funzionare mentre si è in attesa di qualcosa da finire. In realtà, si dovrebbe avere una funzione più simile a questo come la vostra funzione di chiamata:
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, avere quattro differenti salvare funzioni di registrazione che vanno allo stesso tempo. MyCallingFunction
si completerà molto più rapidamente utilizzando async await
se poi le singole funzioni SaveRecords
sono state chiamate in serie.
L'unica cosa che non ho ancora toccato è la parola chiave await
. Ciò che fa è interrompere l'esecuzione della funzione corrente fino al completamento di Task
che si è in attesa. Pertanto, nel caso dell'originale MyCallingFunction
, la riga Function Complete
non verrà scritta sulla console fino al termine della funzione SaveRecords
.
Per farla breve, se si dispone di un'opzione per utilizzare async await
, si dovrebbe aumentare notevolmente le prestazioni della vostra applicazione.
async è molto, * molto * più di quello che impedisce al thread dell'interfaccia utente del client di bloccare nelle applicazioni client. Sono sicuro che ci sarà una risposta esperta a breve. – jdphenix