Cette page vous a-t-elle été utile ?
Votre avis sur ce contenu est important. N'hésitez pas à nous faire part de vos commentaires.
Vous avez d'autres commentaires ?
1500 caractères restants
Exporter (0) Imprimer
Développer tout
Cet article a fait l'objet d'une traduction manuelle. Déplacez votre pointeur sur les phrases de l'article pour voir la version originale de ce texte.
Traduction
Source

Utilisation d'un contrôleur asynchrone dans ASP.NET MVC

La classe AsyncController vous permet d'écrire des méthodes d'action asynchrones. Vous pouvez utiliser des méthodes d'action asynchrones pour des requêtes à durée d'exécution longue et n'utilisant pas le processeur de manière intensive. Cela permet de ne pas bloquer le serveur Web pendant le traitement de la requête. Les appels de services Web à durée d'exécution longue constituent une utilisation typique de la classe AsyncController.

Cette rubrique contient les sections suivantes :

Un projet Visual Studio contenant du code source est disponible pour accompagner cette rubrique : Télécharger.

Sur le serveur Web, le .NET Framework gère un pool de threads utilisés pour traiter des requêtes ASP.NET. À l'arrivée d'une requête, un thread du pool est distribué pour la traiter. Si la requête est traitée de façon synchrone, le thread qui traite cette requête est bloqué pendant le traitement, et ne peut pas en traiter d'autre.

Cela peut ne pas poser de problème, car le pool de threads peut tout à fait être agrandi afin de contenir de nombreux threads bloqués. Toutefois, le nombre de threads dans le pool est limité. Dans les grandes applications qui traitent plusieurs requêtes simultanées à durée d'exécution longue, tous les threads disponibles peuvent être bloqués. Cet état est appelé privation de thread. Dans cet état, le serveur Web met des requêtes en file d'attente. Si la file d'attente des requêtes arrive à saturation, le serveur Web rejette les requêtes en indiquant l'état HTTP 503 (serveur encombré).

Dans les applications dans lesquelles la privation de thread peut se produire, vous pouvez configurer des actions devant être traitées de façon asynchrone. Une requête asynchrone demande la même durée de traitement qu'une requête synchrone. Par exemple, si une requête passe un appel de réseau d'une durée de deux secondes, la requête prend alors deux secondes (qu'elle soit exécutée de façon synchrone ou asynchrone). Toutefois, lors d'un appel asynchrone, le serveur peut tout à fait répondre à d'autres requêtes pendant qu'il attend la fin de la première requête. Par conséquent, les requêtes asynchrones empêchent la mise en file d'attente des requêtes lorsque de nombreuses requêtes appellent des opérations à durée d'exécution longue.

Lorsqu'une action asynchrone est appelée, les étapes suivantes se produisent :

  1. Le serveur Web obtient un thread du pool (thread de travail) et le planifie pour gérer une requête entrante. Ce thread de travail initialise une opération asynchrone.

  2. Le thread de travail est retourné au pool de threads pour traiter une autre requête Web.

  3. Une fois l'opération asynchrone terminée, il en notifie ASP.NET.

  4. Le serveur Web obtient un thread de travail du pool (il peut s'agir d'un thread différent de celui qui a démarré l'opération asynchrone) pour traiter le reste de la requête, notamment le rendu de la réponse.

L'illustration suivante présente le modèle asynchrone.

Pipeline asynchrone

Cette section répertorie des directives relatives aux situations dans lesquelles utiliser les méthodes d'action synchrones ou asynchrones. Il ne s'agit que de directives ; vous devez examiner chaque application individuellement pour déterminer si les méthodes d'action asynchrones permettent d'améliorer les performances.

En règle générale, utilisez des pipelines synchrones lorsque les conditions suivantes sont remplies :

  • Les opérations sont simples ou à durée d'exécution courte.

  • La simplicité est plus importante que l'efficacité.

  • Les opérations sont essentiellement des opérations UC, et non des opérations qui impliquent une surcharge du disque ou du réseau. L'utilisation de méthodes d'action asynchrones sur des opérations utilisant le processeur de manière intensive ne présente aucun avantage et donne lieu à une surcharge plus importante.

En règle générale, utilisez des pipelines asynchrones lorsque les conditions suivantes sont remplies :

  • Les opérations utilisent le réseau ou les E/S de manière intensive et non le processeur.

  • Les tests montrent que les opérations bloquantes forment un goulet d'étranglement au niveau des performances de site et qu'IIS peut traiter plus de requêtes en utilisant des méthodes d'action asynchrones pour ces appels bloquants.

  • Le parallélisme est plus important que la simplicité du code.

  • Vous souhaitez fournir un mécanisme qui permet aux utilisateurs d'annuler une requête à durée d'exécution longue.

L'exemple téléchargeable indique comment utiliser efficacement des méthodes d'action asynchrones. L'exemple de programme appelle la méthode Sleep pour simuler un processus à durée d'exécution longue. Peu d'applications de production présentent ce type d'avantages exceptionnels en utilisant des méthodes d'action asynchrones.

Vous devez tester les applications pour déterminer si les méthodes asynchrones fournissent un avantage en matière de performances. Dans certains cas, il peut se révéler préférable d'augmenter les requêtes simultanées maximales d'IIS par UC, ainsi que le nombre maximal de threads simultanés par UC. Pour plus d'informations sur la configuration des threads ASP.NET, consultez l'entrée Utilisation de thread ASP.NET sur IIS 7.0 et 6.0 (page éventuellement en anglais) sur le blog de Thomas Marquardt. Pour plus d'informations sur les situations dans lesquelles passer des appels à des bases de données asynchrones, consultez l'entrée Mes appels de base de données doivent-ils être asynchrones ? (page éventuellement en anglais) sur le blog de Rick Anderson.

Peu d'applications requièrent que toutes leurs méthodes d'action soient asynchrones. Souvent, la conversion de quelques méthodes d'action synchrones en méthodes asynchrones offre le meilleur rapport performances/travail requis.

L'exemple de code suivant présente une méthode d'action synchrone utilisée pour afficher des actualités à partir d'un contrôleur de portail. La requête Portal/News?city=Seattle affiche des actualités concernant Seattle.

public class PortalController: Controller {
    public ActionResult News(string city) {
        NewsService newsService = new NewsService();
        ViewStringModel headlines = newsService.GetHeadlines(city);
        return View(headlines);
    }
}

L'exemple suivant montre la méthode d'action News réécrite sous la forme d'une méthode asynchrone.

public class PortalController : AsyncController {
    public void NewsAsync(string city) {

        AsyncManager.OutstandingOperations.Increment();
        NewsService newsService = new NewsService();
        newsService.GetHeadlinesCompleted += (sender, e) =>
        {
            AsyncManager.Parameters["headlines"] = e.Value;
            AsyncManager.OutstandingOperations.Decrement();
        };
        newsService.GetHeadlinesAsync(city);
    }

    public ActionResult NewsCompleted(string[] headlines) {
        return View("News", new ViewStringModel
        {
            NewsHeadlines = headlines
        });
    }
}

La conversion d'une méthode d'action synchrone en une méthode d'action asynchrone implique les étapes suivantes :

  1. Au lieu de dériver le contrôleur de Controller, dérivez-le de AsyncController. Les contrôleurs qui dérivent de AsyncController permettent à ASP.NET de traiter des requêtes asynchrones, et ils peuvent toujours traiter les méthodes d'action synchrones.

  2. Créez deux méthodes pour l'action. La méthode qui initialise le processus asynchrone doit disposer d'un nom qui se compose de l'action et du suffixe « Async ». La méthode appelée à la fin du processus asynchrone (méthode de rappel) doit avoir un nom qui se compose de l'action et du suffixe « Completed ». Dans l'exemple précédent, la méthode News a été convertie en deux méthodes : NewsAsync et NewsCompleted.

    La méthode NewsAsync retourne void (aucune valeur dans Visual Basic). La méthode NewsCompleted retourne une instance ActionResult. Bien que l'action se compose de deux méthodes, elle est accessible à l'aide de la même URL que pour une méthode d'action synchrone (par exemple, Portal/News?city=Seattle). Des méthodes telles que RedirectToAction et RenderAction font également référence à la méthode d'action telle que News, et non pas à NewsAsync.

    Les paramètres passés à NewsAsync utilisent les mécanismes de liaison de paramètres normaux. Les paramètres passés à NewsCompleted utilisent le dictionnaire Parameters.

  3. Remplacez l'appel synchrone dans la méthode ActionResult d'origine par un appel asynchrone dans la méthode d'action asynchrone. Dans l'exemple ci-dessus, l'appel à newsService.GetHeadlines est remplacé par un appel à newsService.GetHeadlinesAsync.

La classe NewsService consommée par la méthode NewsAsync est un exemple de service qui expose des méthodes à l'aide d'un modèle asynchrone basé sur les événements. Pour plus d'informations sur ce modèle, consultez Event-based Asynchronous Pattern Overview.

La propriété OutstandingOperations notifie ASP.NET du nombre d'opérations en attente. Cela est nécessaire, car ASP.NET ne peut pas déterminer le nombre d'opérations initialisées par la méthode d'action, ni le moment où ces opérations sont terminées. Lorsque la propriété OutstandingOperations a la valeur zéro, ASP.NET exécute l'opération asynchrone globale en appelant la méthode NewsCompleted.

Notez les éléments suivants concernant les méthodes d'action asynchrones :

  • Si le nom de l'action est Sample, l'infrastructure recherche les méthodes SampleAsync et SampleCompleted.

  • Les pages de vue doivent se voir affecter le nom Sample.aspx, plutôt que SampleAsync.aspx ou SampleCompleted.aspx. (Le nom de l'action est Sample, et non SampleAsync.)

  • Un contrôleur ne peut pas contenir de méthode asynchrone nommée SampleAsync, ni de méthode synchrone nommée Sample. Si c'est tout de même le cas, une exception AmbiguousMatchException est levée car les méthodes d'action SampleAsync et Sample ont la même signature de requête.

Les méthodes d'action asynchrones se révèlent particulièrement utiles lorsqu'une action doit exécuter plusieurs opérations indépendantes. Par exemple, un site portail peut afficher non seulement des actualités, mais également des données relatives au sport, à la météo et à la Bourse, entre autres.

L'exemple suivant présente une version synchrone de la méthode d'action Index du portail d'informations.

public ActionResult IndexSynchronous( string city ) {
    
    NewsService newsService = new NewsService();
    string[] headlines = newsService.GetHeadlines();

    SportsService sportsService = new SportsService();
    string[] scores = sportsService.GetScores();

    WeatherService weatherService = new WeatherService();
    string[] forecast = weatherService.GetForecast();

    return View("Common", new PortalViewModel  {
        NewsHeadlines = headlines,
        SportsScores = scores,
        Weather = forecast
    });
}

Les appels à chaque service sont passés séquentiellement. Par conséquent, la durée nécessaire à la réponse à la requête correspond à la somme de chaque appel de service, à laquelle s'ajoute une petite surcharge. Par exemple, si les appels prennent 400, 500 et 600 millisecondes, le temps de réponse total est légèrement supérieur à 1,5 seconde. Toutefois, si les appels de service sont passés de façon asynchrone (en parallèle), le temps de réponse total est légèrement supérieur à 600 millisecondes, car il s'agit de la durée de la tâche la plus longue.

L'exemple suivant présente une version asynchrone de la méthode d'action Index du portail d'informations.

public void IndexAsync(string city) {
    AsyncManager.OutstandingOperations.Increment(3);

    NewsService newsService = new NewsService();
    newsService.GetHeadlinesCompleted += (sender, e) =>
    {
        AsyncManager.Parameters["headlines"] = e.Value;
        AsyncManager.OutstandingOperations.Decrement();
    };
    newsService.GetHeadlinesAsync();

    SportsService sportsService = new SportsService();
    sportsService.GetScoresCompleted += (sender, e) =>
    {
        AsyncManager.Parameters["scores"] = e.Value;
        AsyncManager.OutstandingOperations.Decrement();
    };
    sportsService.GetScoresAsync();

    WeatherService weatherService = new WeatherService();
    weatherService.GetForecastCompleted += (sender, e) =>
    {
        AsyncManager.Parameters["forecast"] = e.Value;
        AsyncManager.OutstandingOperations.Decrement();
    };
    weatherService.GetForecastAsync();
}

public ActionResult IndexCompleted(string[] headlines, string[] scores, string[] forecast) {
    return View("Common", new PortalViewModel  {
        NewsHeadlines = headlines,
        SportsScores = scores,
        Weather = forecast
    });
}          
}

Dans l'exemple précédent, la méthode Increment est appelée avec un paramètre de 3, car trois opérations asynchrones coexistent.

Si vous souhaitez appliquer des attributs à une méthode d'action asynchrone, appliquez-les à la méthode ActionAsync plutôt qu'à la méthode ActionCompleted. Les attributs sur la méthode ActionCompleted sont ignorés.

Deux nouveaux attributs ont été ajoutés : AsyncTimeoutAttribute et NoAsyncTimeoutAttribute. Ces attributs vous permettent de contrôler le délai d'attente asynchrone.

Si une méthode d'action asynchrone appelle un service qui expose des méthodes à l'aide du modèle BeginMethod/EndMethod, la méthode de rappel (autrement dit, la méthode passée en tant que paramètre de rappel asynchrone à la méthode Begin) peut s'exécuter sur un thread qui n'est pas sous le contrôle d'ASP.NET. Dans ce cas, HttpContext.Current a la valeur null, et l'application peut rencontrer des conditions de concurrence lorsqu'elle accède aux membres de la classe AsyncManager, comme Parameters. Pour vérifier que vous disposez d'un accès à l'instance HttpContext.Current et pour éviter la condition de concurrence, vous pouvez restaurer HttpContext.Current en appelant Sync() à partir de la méthode de rappel.

Si le rappel s'effectue de façon synchrone, il est exécuté sur un thread qui est sous le contrôle d'ASP.NET, et les opérations sont sérialisées afin d'éliminer tout problème de concurrence éventuel. L'appel de Sync() à partir d'un thread qui est déjà sous le contrôle d'ASP.NET présente un comportement indéfini.

La méthode ActionCompleted est toujours appelée sur un thread qui est sous le contrôle d'ASP.NET. Par conséquent, n'appelez pas Sync() à partir de cette méthode.

Le rappel que vous passez à la méthode Begin peut être appelé à l'aide d'un thread qui est sous le contrôle d'ASP.NET. Par conséquent, vous devez vérifier cette condition avant d'appeler Sync(). Si l'opération s'est exécutée de façon synchrone (autrement dit, si CompletedSynchronously a la valeur true), le rappel s'exécute sur le thread d'origine, et vous n'avez pas à appeler Sync(). Si l'opération s'est exécutée de façon asynchrone (autrement dit, si CompletedSynchronously a la valeur false), le rappel s'exécute sur un pool de threads ou sur le thread du port de terminaison d'E/S, et vous devez appeler Sync().

Pour plus d'informations sur le modèle BeginMethod/EndMethod, consultez Asynchronous Programming Overview et l'entrée Utilisation du modèle BeginMethod/EndMethod avec MVC (page éventuellement en anglais) sur le blog de Rick Anderson.

Le tableau suivant répertorie les classes clés pour les méthodes d'action asynchrones.

class

Description

AsyncController

Fournit la classe de base pour les contrôleurs asynchrones.

AsyncManager

Fournit des opérations asynchrones pour la classe AsyncController.

Ajouts de la communauté

AJOUTER
Microsoft réalise une enquête en ligne pour recueillir votre opinion sur le site Web de MSDN. Si vous choisissez d’y participer, cette enquête en ligne vous sera présentée lorsque vous quitterez le site Web de MSDN.

Si vous souhaitez y participer,
Afficher:
© 2015 Microsoft