Chaque fois que vous devez effectuer une action sur un serveur distant, votre programme génère la requête, l'envoie, puis attend une réponse. Je vais utiliser SaveChanges()et SaveChangesAsync()comme exemple, mais il en va de même pour Find()et FindAsync().
Supposons que vous ayez une liste myListde plus de 100 éléments que vous devez ajouter à votre base de données. Pour insérer cela, votre fonction ressemblerait à ceci:
using(var context = new MyEDM())
{
context.MyTable.AddRange(myList);
context.SaveChanges();
}
Commencez par créer une instance de MyEDM, ajoutez la liste myListà la table MyTable, puis appelez SaveChanges()pour conserver les modifications apportées à la base de données. Cela fonctionne comme vous le souhaitez, les enregistrements sont validés, mais votre programme ne peut rien faire d'autre tant que la validation n'est pas terminée. Cela peut prendre du temps en fonction de ce que vous vous engagez. Si vous validez des modifications dans les enregistrements, l'entité doit les valider un par un (une fois, une sauvegarde a pris 2 minutes pour les mises à jour)!
Pour résoudre ce problème, vous pouvez effectuer l'une des deux opérations suivantes. Le premier est que vous pouvez démarrer un nouveau fil pour gérer l'insert. Bien que cela libère le thread appelant pour continuer à s'exécuter, vous avez créé un nouveau thread qui va simplement rester là et attendre. Il n'y a pas besoin de cette surcharge, et c'est ce que le async awaitmodèle résout.
Pour les opérations d'E / S, awaitdevient rapidement votre meilleur ami. En prenant la section de code ci-dessus, nous pouvons la modifier pour qu'elle soit:
using(var context = new MyEDM())
{
Console.WriteLine("Save Starting");
context.MyTable.AddRange(myList);
await context.SaveChangesAsync();
Console.WriteLine("Save Complete");
}
C'est un tout petit changement, mais il y a des effets profonds sur l'efficacité et les performances de votre code. Alors que se passe-t-il? Le début du code est le même, vous créez une instance de MyEDMet ajoutez votre myListà MyTable. Mais lorsque vous appelez await context.SaveChangesAsync(), l'exécution du code revient à la fonction appelante! Ainsi, pendant que vous attendez que tous ces enregistrements soient validés, votre code peut continuer à s'exécuter. Dites que la fonction qui contenait le code ci-dessus avait la signature de public async Task SaveRecords(List<MyTable> saveList), la fonction appelante pourrait ressembler à ceci:
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");
}
Pourquoi vous auriez une fonction comme celle-ci, je ne sais pas, mais ce qu'elle produit montre comment async awaitfonctionne. Voyons d'abord ce qui se passe.
L'exécution entre MyCallingFunction, Function Startingpuis Save Startingest écrite sur la console, puis la fonction SaveChangesAsync()est appelée. À ce stade, l'exécution retourne MyCallingFunctionet entre dans la boucle for en écrivant «Continuer à exécuter» jusqu'à 1000 fois. Une fois SaveChangesAsync()terminé, l'exécution revient à la SaveRecordsfonction, en écrivant Save Completedans la console. Une fois que tout est SaveRecordsterminé, l'exécution se poursuivra MyCallingFunctioncomme elle l'était à la SaveChangesAsync()fin. Confus? Voici un exemple de sortie:
Démarrage de la fonction
Enregistrer le départ
Continuer à exécuter!
Continuer à exécuter!
Continuer à exécuter!
Continuer à exécuter!
Continuer à exécuter!
....
Continuer à exécuter!
Sauvegardez terminé!
Continuer à exécuter!
Continuer à exécuter!
Continuer à exécuter!
....
Continuer à exécuter!
Fonction terminée!
Ou peut-être:
Démarrage de la fonction
Enregistrer le départ
Continuer à exécuter!
Continuer à exécuter!
Sauvegardez terminé!
Continuer à exécuter!
Continuer à exécuter!
Continuer à exécuter!
....
Continuer à exécuter!
Fonction terminée!
C'est la beauté de async await, votre code peut continuer à s'exécuter pendant que vous attendez que quelque chose se termine. En réalité, vous auriez une fonction plus comme celle-ci que votre fonction d'appel:
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());
}
Ici, vous disposez de quatre fonctions de sauvegarde d'enregistrement différentes en même temps . MyCallingFunctionse terminera beaucoup plus rapidement async awaitque si les SaveRecordsfonctions individuelles étaient appelées en série.
La seule chose que je n'ai pas encore abordée est le awaitmot clé. Cela empêche la fonction en cours de s'exécuter jusqu'à ce que tout ce que Taskvous attendez soit terminé. Donc, dans le cas de l'original MyCallingFunction, la ligne Function Completene sera pas écrite sur la console tant que la SaveRecordsfonction ne sera pas terminée.
Pour faire court, si vous avez une option à utiliser async await, vous devriez car cela augmentera considérablement les performances de votre application.