Jedes Mal, wenn Sie eine Aktion auf einem Remote-Server ausführen müssen, generiert Ihr Programm die Anforderung, sendet sie und wartet auf eine Antwort. Ich werde SaveChanges()und SaveChangesAsync()als Beispiel verwenden, aber das gleiche gilt für Find()und FindAsync().
Angenommen, Sie haben eine Liste myListmit mehr als 100 Elementen, die Sie Ihrer Datenbank hinzufügen müssen. Um das einzufügen, würde Ihre Funktion ungefähr so aussehen:
using(var context = new MyEDM())
{
context.MyTable.AddRange(myList);
context.SaveChanges();
}
Zuerst erstellen Sie eine Instanz von MyEDM, fügen die Liste myListder Tabelle hinzu MyTableund rufen dann auf, SaveChanges()um die Änderungen an der Datenbank beizubehalten. Es funktioniert wie Sie möchten, die Datensätze werden festgeschrieben, aber Ihr Programm kann nichts anderes tun, bis das Festschreiben abgeschlossen ist. Dies kann lange dauern, je nachdem, was Sie festlegen. Wenn Sie Änderungen an den Datensätzen vornehmen, muss die Entität diese nacheinander festschreiben (ich hatte einmal 2 Minuten Zeit für Aktualisierungen, um sie zu speichern)!
Um dieses Problem zu lösen, können Sie eines von zwei Dingen tun. Das erste ist, dass Sie einen neuen Thread starten können, um die Einfügung zu handhaben. Dadurch wird der aufrufende Thread für die weitere Ausführung freigegeben. Sie haben jedoch einen neuen Thread erstellt, der nur dort sitzt und wartet. Es gibt keine Notwendigkeit für diesen Aufwand, und das ist, was dieasync await löst Muster.
Für E / A-Operationen wird awaitschnell Ihr bester Freund. Wenn wir den Codeabschnitt von oben nehmen, können wir ihn wie folgt ändern:
using(var context = new MyEDM())
{
Console.WriteLine("Save Starting");
context.MyTable.AddRange(myList);
await context.SaveChangesAsync();
Console.WriteLine("Save Complete");
}
Es ist eine sehr kleine Änderung, aber es gibt tiefgreifende Auswirkungen auf die Effizienz und Leistung Ihres Codes. Was passiert also? Der Beginn des Codes ist das gleiche, Sie eine Instanz erstellen MyEDMund fügen Sie Ihre myListzu MyTable. Wenn Sie jedoch aufrufen await context.SaveChangesAsync(), kehrt die Ausführung des Codes zur aufrufenden Funktion zurück!Während Sie darauf warten, dass alle diese Datensätze festgeschrieben werden, kann Ihr Code weiterhin ausgeführt werden. Angenommen, die Funktion, die den obigen Code enthielt, hatte die Signatur public async Task SaveRecords(List<MyTable> saveList), die aufrufende Funktion könnte folgendermaßen aussehen:
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");
}
Warum Sie eine solche Funktion haben würden, weiß ich nicht, aber was sie ausgibt, zeigt, wie es async awaitfunktioniert. Lassen Sie uns zuerst untersuchen, was passiert.
Die Ausführung wird eingegeben MyCallingFunction, Function Startingdann Save Startingin die Konsole geschrieben und die Funktion SaveChangesAsync()aufgerufen. Zu diesem Zeitpunkt kehrt die Ausführung zu MyCallingFunctionder for-Schleife zurück und gibt sie bis zu 1000 Mal ein. Wenn der SaveChangesAsync()Vorgang abgeschlossen ist, kehrt die Ausführung zur SaveRecordsFunktion zurück und schreibt Save Completein die Konsole. Sobald alles SaveRecordsabgeschlossen ist, wird die Ausführung in fortgesetztMyCallingFunction genau dort sie abgeschlossen war SaveChangesAsync(). Verwirrt? Hier ist eine Beispielausgabe:
Funktionsstart
Speichern Start
Weiter ausführen!
Weiter ausführen!
Weiter ausführen!
Weiter ausführen!
Weiter ausführen!
....
Weiter ausführen!
Speichern Sie vollständig!
Weiter ausführen!
Weiter ausführen!
Weiter ausführen!
....
Weiter ausführen!
Funktion abgeschlossen!
Oder vielleicht:
Funktionsstart
Speichern Start
Weiter ausführen!
Weiter ausführen!
Speichern Sie vollständig!
Weiter ausführen!
Weiter ausführen!
Weiter ausführen!
....
Weiter ausführen!
Funktion abgeschlossen!
Das ist das Schöne daran: async awaitIhr Code kann weiterhin ausgeführt werden, während Sie darauf warten, dass etwas beendet wird. In Wirklichkeit hätten Sie eine ähnliche Funktion als Ihre aufrufende Funktion:
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());
}
Hier haben Sie vier verschiedene Funktionen zum Speichern von Datensätzen gleichzeitig . MyCallingFunctionwird viel schneller ausgeführt, async awaitals wenn die einzelnen SaveRecordsFunktionen in Reihe aufgerufen würden.
Das einzige, was ich noch nicht angesprochen habe, ist das awaitSchlüsselwort. Dadurch wird die Ausführung der aktuellen Funktion gestoppt, bis alles Task, was Sie erwarten, abgeschlossen ist. Im Fall des Originals MyCallingFunctionwird die Zeile Function Completealso erst nach Abschluss der SaveRecordsFunktion in die Konsole geschrieben .
Kurz gesagt, wenn Sie eine Option zur Verwendung haben async await, sollten Sie dies tun, da dies die Leistung Ihrer Anwendung erheblich steigern wird.