La programmation asynchrone "grandit" à travers la base de code. Il a été comparé à un virus zombie . La meilleure solution est de lui permettre de se développer, mais parfois ce n'est pas possible.
J'ai écrit quelques types dans ma bibliothèque Nito.AsyncEx pour gérer une base de code partiellement asynchrone. Il n'y a cependant pas de solution qui fonctionne dans toutes les situations.
Solution A
Si vous avez une méthode asynchrone simple qui n'a pas besoin de se synchroniser avec son contexte, vous pouvez utiliser Task.WaitAndUnwrapException:
var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();
Vous ne pas à utiliser Task.Waitou Task.Resultparce qu'ils enveloppent exceptions AggregateException.
Cette solution n'est appropriée que si elle MyAsyncMethodne se synchronise pas avec son contexte. En d'autres termes, chaque awaitentrée MyAsyncMethoddoit se terminer par ConfigureAwait(false). Cela signifie qu'il ne peut pas mettre à jour les éléments de l'interface utilisateur ni accéder au contexte de demande ASP.NET.
Solution B
Si vous MyAsyncMethoddevez vous synchroniser avec son contexte, vous pouvez utiliser AsyncContext.RunTaskpour fournir un contexte imbriqué:
var result = AsyncContext.RunTask(MyAsyncMethod).Result;
* Mise à jour du 14/04/2014: dans les versions plus récentes de la bibliothèque, l'API est la suivante:
var result = AsyncContext.Run(MyAsyncMethod);
(Il est correct d'utiliser Task.Resultdans cet exemple car RunTaskpropage des Taskexceptions).
La raison pour laquelle vous pourriez avoir besoin à la AsyncContext.RunTaskplace Task.WaitAndUnwrapExceptionest à cause d'une possibilité de blocage plutôt subtile qui se produit sur WinForms / WPF / SL / ASP.NET:
- Une méthode synchrone appelle une méthode asynchrone, obtenant un
Task.
- La méthode synchrone fait une attente de blocage sur le
Task.
- La
asyncméthode utilise awaitsans ConfigureAwait.
- Le
Taskne peut pas terminer dans cette situation car il se termine uniquement lorsque la asyncméthode est terminée; la asyncméthode ne peut pas se terminer car elle tente de planifier sa continuation vers le SynchronizationContext, et WinForms / WPF / SL / ASP.NET n'autorisera pas la poursuite à s'exécuter car la méthode synchrone s'exécute déjà dans ce contexte.
C'est une des raisons pour lesquelles c'est une bonne idée d'utiliser autant que possible ConfigureAwait(false)dans chaque asyncméthode.
Solution C
AsyncContext.RunTaskne fonctionnera pas dans tous les scénarios. Par exemple, si la asyncméthode attend quelque chose qui nécessite un événement d'interface utilisateur, vous bloquerez même avec le contexte imbriqué. Dans ce cas, vous pouvez démarrer la asyncméthode sur le pool de threads:
var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();
Cependant, cette solution nécessite un MyAsyncMethodqui fonctionnera dans le contexte du pool de threads. Il ne peut donc pas mettre à jour les éléments de l'interface utilisateur ni accéder au contexte de demande ASP.NET. Et dans ce cas, vous pouvez tout aussi bien compléter ConfigureAwait(false)ses awaitdéclarations et utiliser la solution A.
Mise à jour, 2019-05-01: Les "pratiques les moins pires" actuelles sont dans un article MSDN ici .