649 votes

Meilleure pratique pour appeler ConfigureAwait pour tout le code côté serveur

Lorsque vous avez un code côté serveur (c'est à dire quelques ApiController) et vos fonctions sont asynchrone afin de renvoyer Task<SomeObject> - est-il considéré comme une bonne pratique que de tout temps, vous attendent les fonctions que vous appelez ConfigureAwait(false)?

J'avais lu qu'il est plus performant puisqu'il n'est pas nécessaire de changer les contextes de thread en arrière à l'original du contexte de thread. Cependant, avec ASP.NET l'Api Web, si votre demande est les prochains sur un fil, et vous attendent de certaines fonctions et appelez - ConfigureAwait(false) qui pourraient vous mettre sur un thread différent lorsque vous êtes de retour sur le résultat final de votre ApiController fonction.

J'ai tapé un exemple de ce dont je parle ci-dessous:

public class CustomerController : ApiController
{
    public async Task<Customer> Get(int id)
    {
        // you are on a particular thread here
        var customer = await SomeAsyncFunctionThatGetsCustomer(id).ConfigureAwait(false);

        // now you are on a different thread!  will that cause problems?
        return customer;
    }
}

735voto

Stephen Cleary Points 91731

Cette vidéo de la ASP.NET l'équipe dispose de la meilleure information sur l'utilisation de l' async sur ASP.NET.

J'avais lu qu'il est plus performant puisqu'il n'est pas nécessaire de changer les contextes de thread en arrière à l'original du contexte de thread.

C'est vrai avec l'INTERFACE utilisateur des applications, où il y a un seul thread d'INTERFACE utilisateur que vous avez à "synchroniser".

Dans ASP.NET la situation est un peu plus complexe. Lorsqu'un async méthode reprend l'exécution, il s'empare d'un thread du ASP.NET pool de threads. Si vous désactivez le contexte de la capture à l'aide de ConfigureAwait(false), alors le fil continue de l'exécution de la méthode directement. Si vous ne désactivez pas le contexte de la capture, puis le fil d'entrer de nouveau dans le contexte de la demande, puis de continuer à exécuter la méthode.

Donc, ConfigureAwait(false) ne vous fait pas gagner un fil de sauter dans ASP.NET; il ne vous économiser le ré-entrant dans le contexte de la demande, mais ce qui est normalement très rapide. ConfigureAwait(false) pourrait être utile si vous essayez de faire une petite quantité de traitement en parallèle d'une demande, mais vraiment TPL est un meilleur ajustement pour la plupart de ces scénarios.

Cependant, avec ASP.NET l'Api Web, si votre demande est les prochains sur un fil, et vous attendent certaine fonction et l'appel ConfigureAwait(faux) qui pourraient vous mettre sur un thread différent lorsque vous êtes de retour sur le résultat final de votre ApiController fonction.

En fait, il suffit de faire un await peut le faire. Une fois que votre async méthode touche un await, la méthode est bloquée, mais le thread retourne pour le pool de threads. Lorsque la méthode est prêt à continuer, n'importe quel thread est, arraché au fil de la piscine et pour reprendre la méthode.

La seule différence ConfigureAwait fait en ASP.NET est de savoir si ce thread entre dans le contexte de la demande lors de la reprise de la méthode.

J'ai plus d'informations dans mon article MSDN sur SynchronizationContext et mon async intro blog.

147voto

tugberk Points 16203

Brève réponse à votre question: Non. Vous ne devez pas appeler ConfigureAwait(false) au niveau de l'application comme ça.

TL;DR version de la réponse longue: Si vous écrivez une bibliothèque où vous ne connaissez pas votre consommation et n'ont pas besoin d'un contexte de synchronisation (qui ne convient pas dans une bibliothèque, je crois), vous devez toujours utiliser ConfigureAwait(false). Sinon, les consommateurs de votre bibliothèque peut faire face à des blocages par la consommation de vos méthodes asynchrones dans un blocage de la mode. Cela dépend de la situation.

Voici un peu plus d'explication détaillée sur l'importance de l' ConfigureAwait méthode (une citation de mon blog):

Lorsque vous êtes en attente sur une méthode avec le mot clé await, compilateur génère des tas de code dans votre nom. L'un des buts de cette l'action est de gérer la synchronisation avec l'INTERFACE utilisateur (ou principal) thread. La clé la composante de cette fonction est l' SynchronizationContext.Currentqui obtient le contexte de synchronisation pour le thread courant. SynchronizationContext.Current est rempli en fonction de la environnement, vous êtes en. L' GetAwaiter méthode de la Tâche de recherche SynchronizationContext.Current. Si l'actuel contexte de synchronisation est pas null, la poursuite qui est transmis à qui awaiter obtiendrez posté le dos pour que la synchronisation contexte.

Lorsque la consommation d'une méthode, qui utilise la nouvelle asynchrone langue caractéristiques, avec un blocage de la mode, vous allez vous retrouver avec un blocage si vous avez un disponible SynchronizationContext. Lorsque vous consommez ces méthodes dans un blocage de la mode (en attente sur la Tâche à Attendre méthode ou de prendre le résultat directement à partir de la propriété du Résultat de la La tâche), vous permettra de bloquer le thread principal en même temps. Lorsque finalement, la Tâche se termine à l'intérieur de la méthode dans le pool de threads, il va invoquer la poursuite de publier sur le thread principal parce qu' SynchronizationContext.Current est disponible et capturé. Mais il y a un problème ici: le thread d'INTERFACE utilisateur est bloqué et que vous avez un impasse!

Enfin, voici deux articles pour vous qui sont exactement à votre question:

Espérons que cette aide.

14voto

Aliostad Points 47792

Je pense que la Tâche de la mise en œuvre est généralement compliquée si pas défectueux. OK enlevé mon coup de gueule.

  1. La tâche est jetable et pourtant nous sommes pas censés utiliser l'aide.
  2. ConfigureAwait a été introduit en 4.5. La tâche a été introduit dans la version 4.0.
  3. .NET Threads toujours utilisé à l'écoulement du contexte (voir C# via CLR livre), mais dans l'implémentation par défaut de Task.ContinueWith ils ne b/c il a été réalisé changement de contexte est cher et il est désactivé par défaut.
  4. Le problème, c'est un développeur de la bibliothèque ne doit pas se préoccuper de savoir si ses clients ont besoin de flux de contexte ou pas, donc il ne devrait pas décider si le flux le contexte ou non.
  5. [Ajouté] Le fait qu'il n'y a pas de réponse faisant autorité et de référence approprié et nous continuer à se battre sur ce signifie que quelqu'un ne l'a pas fait son travail comme il faut.

J'ai eu quelques posts sur le sujet, mais de mon point de vue - en plus de Tugberk est agréable réponse est que vous devez tourner toutes les Api asynchrone et idéalement flux le contexte . Puisque vous faites asynchrone, vous pouvez simplement utiliser les continuations au lieu d'attendre donc pas de blocage en être la cause car pas d'attente se fait dans la bibliothèque et vous gardez le fluide de sorte que le contexte est conservé (comme HttpContext).

Problème, c'est quand une bibliothèque expose une API synchrones, mais utilise un autre asynchrone à l'API, par conséquent, vous devez utiliser Wait()/Result dans votre code.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X