Meilleures pratiques en programmation asynchrone .Net

La programmation asynchrone vous permet d'effectuer des opérations d'E / S gourmandes en ressources sans avoir à bloquer sur le thread principal ou en cours d'exécution de l'application. Bien que bénéfique et apparemment facile à mettre en œuvre, il comporte beaucoup de complexité et de risques. Les risques potentiels associés à la programmation asynchrone, en particulier en utilisant la programmation asynchrone de manière incorrecte en ne suivant pas les pratiques recommandées, incluent les blocages, les plantages de processus et même la lenteur des performances. Vous devez également maîtriser l'écriture et le débogage de code asynchrone.

Évitez d'avoir un type de retour void dans les méthodes asynchrones

Une méthode en C # devient une méthode asynchrone à l'aide du mot clé async dans la signature de méthode. Vous pouvez avoir un ou plusieurs mots clés en attente dans une méthode asynchrone. Le mot clé await est utilisé pour désigner le point de suspension. Une méthode async en C # peut avoir l'un de ces types de retour: Task, Task et void. Le mot clé "await" est utilisé dans une méthode asynchrone pour informer le compilateur que la méthode peut avoir un point de suspension et de reprise.

Notez que lorsque vous utilisez le TPL, l'équivalent de renvoyer void dans TPL est async Task. Vous devez savoir que async void est et ne doit être utilisé que pour les événements async. Si vous l'utilisez ailleurs, vous risquez de rencontrer des erreurs. En d'autres termes, une méthode asynchrone qui a void comme type de retour n'est pas recommandée. car les méthodes async qui retournent void ont une sémantique différente lorsque vous travaillez avec des exceptions dans votre application.

Lorsqu'une exception se produit dans une méthode asynchrone dont le type de retour est Tâche ou Tâche, l'objet d'exception est stocké dans l'objet Task. Au contraire, si vous avez une méthode async avec un type de retour void, aucun objet Task n'est associé. Ces exceptions sont déclenchées sur le SynchronizationContext qui était actif au moment où la méthode asynchrone a été appelée. En d'autres termes, vous ne pouvez pas gérer les exceptions levées dans une méthode async void à l'aide de gestionnaires d'exceptions écrits dans la méthode asynchrone. Les méthodes async qui ont un type de retour void sont également difficiles à tester en raison de cette différence de sémantique de gestion des erreurs. Pour votre information, la classe SynchronizationContext dans l'espace de noms System.Threading représente un contexte de synchronisation dans .Net et vous aide à mettre une tâche en file d'attente dans un autre contexte.

La liste de codes suivante illustre cela. Vous avez deux méthodes à savoir, Test et TestAsync et ce dernier lève une exception.

public class AsyncDemo

   {

       public void Test()

       {

           try

           {

               TestAsync();

           }

           catch (Exception ex)

           {

               Console.WriteLine(ex.Message);

           }

       }

       private async void TestAsync()

       {

           throw new Exception("This is an error message");

       }

   }

Voici comment créer une instance de la classe AsyncDemo et appeler la méthode Test.

static void Main(string[] args)

       {

           AsyncDemo obj = new AsyncDemo();

           obj.Test();

           Console.Read();

       }

La méthode de test effectue un appel à la méthode TestAsync et l'appel est encapsulé dans un bloc try-catch avec l'intention de gérer l'exception levée à l'intérieur de la méthode TestAsync. Cependant, l'exception lancée à l'intérieur de la méthode TestAsync ne sera jamais interceptée, c'est-à-dire gérée dans la méthode de l'appelant Test.

Évitez de mélanger du code asynchrone et synchrone

Vous ne devriez jamais avoir un mélange de code synchrone et asynchrone. C'est une mauvaise pratique de programmation de bloquer le code asynchrone en effectuant des appels à Task.Wait ou Task.Result. Je recommanderais d'utiliser le code asynchrone de bout en bout - c'est le moyen le plus sûr d'éviter les erreurs de s'infiltrer.

Vous pouvez éviter les blocages en utilisant. ConfigureAwait(continueOnCapturedContext: false)chaque fois que vous passez un appel pour attendre. Si vous ne l'utilisez pas, la méthode async se bloquerait au point où await a été appelé. Dans ce cas, vous informez simplement le serveur de ne pas capturer le contexte actuel. Je dirais que c'est une bonne pratique d'utiliser .ConfigureAwait (false) sauf si vous avez une raison spécifique de ne pas l'utiliser.

Je discuterais plus en détail de la programmation asynchrone dans mes futurs articles de blog ici. Pour plus d'informations sur les meilleures pratiques en matière de programmation asynchrone, vous pouvez vous référer à l'excellent article de Stephen Cleary sur MSDN.