Apprendre SynchronizationContext, async et attendre

La programmation asynchrone est une forme de programmation parallèle qui vous permet d'exécuter des tâches distinctes du thread d'application principal, puis notifie le thread lorsque son exécution est terminée. L'asynchronie vous aide à exécuter des tâches sans avoir à ralentir le flux d'exécution ou la réactivité de votre application.

Microsoft a pris en charge la programmation parallèle dans .Net Framework pour tirer parti des avantages des systèmes multicœurs. Vous pouvez tirer parti de l'asynchronie pour améliorer les performances et la réactivité de votre application.

Il existe essentiellement deux types d'opérations possibles dans une application. Celles-ci incluent les opérations liées au calcul et aux E / S. Les opérations liées au calcul sont celles dans lesquelles le calcul peut être effectué sur un thread séparé afin que le thread principal puisse continuer son exécution. Au contraire, les opérations liées aux E / S sont celles dans lesquelles elles sont exécutées en externe et n'ont donc pas besoin de bloquer le thread actuel pendant que les E / S sont en cours.

Contexte de synchronisation et contexte d'exécution

Chaque thread a un contexte qui lui est associé - ceci est également connu sous le nom de contexte "courant" - et ces contextes peuvent être partagés entre les threads. Le ExecutionContext contient des métadonnées pertinentes de l'environnement actuel ou du contexte dans lequel le programme est en cours d'exécution. SynchronizationContext représente une abstraction - il indique l'emplacement où le code de votre application est exécuté.

Un SynchronizationContext vous permet de mettre une tâche en file d'attente dans un autre contexte. Notez que chaque thread peut avoir son propre SynchronizatonContext. La classe SynchronizationContext a été ajoutée récemment à l'espace de noms System.Threading et facilite la communication entre les threads. Vous pouvez en savoir plus sur SynchronizationContext et ExecutionContext ici.

Une plongée profonde dans Async et Await

Les trois modèles de programmation asynchrones sont les suivants:

  1. Modèle de programmation asynchrone (APM)
  2. Modèle asynchrone basé sur les événements (EAP)
  3. Modèle asynchrone basé sur les tâches (TAP)

Le dernier, le recommandé et aussi le plus élégant de tous est le TAP.

Notez que vous pouvez marquer une méthode à l'aide du mot clé "async" qui renvoie void, Task ou Task. Notez que lorsqu'une exception se produit dans une méthode asynchrone dont le type de retour est Task ou Task, les détails de l'exception sont stockés dans l'instance Task.

Au contraire, lorsqu'une exception se produit dans une méthode asynchrone dont le type de retour est void, les détails de l'exception sont stockés dans le SynchronizationContext qui était actif au moment où la méthode asynchrone a été appelée. En substance, vous ne pouvez pas gérer les exceptions déclenchées dans une méthode asynchrone ayant un type de retour void à l'aide de gestionnaires d'exceptions écrits dans la méthode asynchrone. En raison des différentes sémantiques de calcul et de gestion des erreurs, il est conseillé d'éviter les méthodes asynchrones ayant des types de retour vides sauf s'il existe une raison suffisante pour les utiliser.

Lorsque vous utilisez le mot-clé "await" dans une méthode asynchrone, la méthode est divisée dans une machine à états. Notez que le mot-clé "await" capture le SynchronizationContext actuel et dès que la tâche qui a été attendue à l'aide du mot-clé "await" est terminée, la machine à états est reprise et l'exécution du code dans la méthode de l'appelant redémarre - c'est aussi connu sous le nom de continuation. Si l'exécution du code qui était attendu à l'aide du mot-clé "await" s'est terminée au moment où le point de suspension est rencontré, la méthode asynchrone (la méthode qui a été marquée comme "async") s'exécute de manière synchrone. Si l'exécution du code attendu n'est pas terminée, un délégué de continuation est attaché au code attendu.

Vous pouvez tirer parti des méthodes asynchrones qui renvoient void pour créer des gestionnaires d'événements asynchrones. La méthode Main ne peut pas être marquée avec le mot-clé "async" car c'est le point d'entrée de l'application - une méthode Main "async" se terminerait au moment où elle est appelée. Le mot-clé "await" informe le compilateur que la méthode peut avoir un point de suspension et de reprise. Incidemment, vous ne pouvez utiliser le mot clé "await" que sur une méthode qui a été marquée comme asynchrone à l'aide du mot clé "async".

Une méthode async lorsqu'elle est appelée, s'exécute de manière synchrone sur le thread actuel quel que soit le type de retour de la méthode. Lorsque vous marquez une méthode comme asynchrone à l'aide du mot clé "async", vous informez simplement le compilateur que la méthode peut être divisée en plusieurs tâches - certaines de ces tâches peuvent s'exécuter de manière asynchrone. De plus, l'inclusion du mot clé «async» dans une méthode ne met pas en file d'attente l'appel de la méthode en tant que partie du pool de threads. L'asynchronie (c'est-à-dire, si une méthode aurait un comportement asynchrone) dépend en fait du point de suspension que vous avez mentionné dans votre méthode en utilisant le mot-clé "await". Si vous n'incluez pas le mot clé "await" dans une méthode asynchrone, la méthode entière s'exécuterait de manière synchrone.