Cet article a fait l'objet d'une traduction automatique.

Programmation asynchrone

Présentation de Async/Await sur ASP.NET

Stephen Cleary

La plupart des ressources en ligne autour d'async/attendent suppose que vous développez des applications de client, mais il async a une place sur le serveur ? La réponse est le plus certainement « oui ». Cet article est une vue d'ensemble conceptuelle des demandes asynchrones sur ASP.NET, mais aussi une référence pour les meilleures ressources en ligne. Je ne pas être couvrant l'async ou attendent la syntaxe ; J'ai déjà fait cela dans un blog liminaire (bit.ly/19IkogW) et dans un article sur les meilleures pratiques async (msdn.microsoft.com/magazine/jj991977). Cet article se concentre plus précisément sur le fonctionne d'async sur ASP.NET.

Pour les applications clientes, telles que Windows Store, bureau Windows et Windows Phone apps, le principal avantage d'async est la réactivité. Ces types d'applications utilisent async principalement pour que l'interface utilisateur réactive. Pour les applications de serveur, le principal avantage d'async est évolutivité. La clé de l'évolutivité de Node.js est son caractère intrinsèquement asynchrone ; Interface Web ouverte pour .NET (OWIN) a été conçu dès le départ pour être asynchrone ; et ASP.NET peut également être asynchrone. Async : Il n'est pas juste pour l'interface utilisateur apps !

Vs synchrones. Traitement de la requête asynchrone

Avant de plonger dans les gestionnaires de demandes asynchrones, je voudrais brièvement en revue demande comment synchrone des gestionnaires travaillent sur ASP.NET. Pour cet exemple, supposons que les demandes du système dépendent de quelque ressource externe, comme une base de données ou les API Web. Lorsqu'une demande arrive, ASP.NET utilise l'un de ses threads du pool et l'assigne à cette demande. Car il est écrit de manière synchrone, le gestionnaire de requêtes appellera cette ressource externe de façon synchrone. Cela bloque le thread de demande jusqu'au retour de l'appel à la ressource externe. Figure 1 illustre un pool de threads avec deux fils, dont un est bloqué en attente d'une ressource externe.

en attente de façon synchrone pour une ressource externe
La figure 1 en attente de façon synchrone pour une ressource externe

Finalement, cet appel de la ressource externe retourne, et le thread de demande reprend le traitement de cette demande. Lorsque la demande est complète et la réponse est prête à être envoyée, le thread de demande est retournée au pool de threads.

C'est très bien, jusqu'à ce que votre serveur ASP.NET obtient plus de demandes qu'il a un filetage à gérer. À ce stade, les demandes supplémentaires sont à attendre pour un thread d'être disponible avant qu'ils puissent fonctionner. La figure 2 illustre le même serveur de deux threads lorsqu'il reçoit trois demandes.

deux threads serveur recevant trois demandes
Figure 2 deux threads serveur recevant trois demandes

Dans ce cas, les deux premières demandes sont assignés threads du pool de threads. Chacune de ces demandes appelle une ressource externe, bloquer les threads. La troisième requête doit attendre pour un thread disponible avant il peut même début de traitement, mais la demande est déjà dans le système. Sa minuterie va, et il est en danger d'erreur HTTP 503 (Service indisponible).

Mais pensez-y un instant : Cette troisième demande est en attente d'un thread, lorsqu'il y a deux autres threads du système effectivement de ne rien faire. Ces threads sont juste bloqués en attente d'un appel externe à retourner. Ils ne font pas n'importe quel vrai travail ; ils ne sont pas en cours d'exécution et ne reçoivent pas de n'importe quel moment de la CPU. Ces threads sont juste gaspillés alors qu'il y a une demande dans le besoin. Il s'agit de la situation visée par les demandes asynchrones.

Gestionnaires de demandes asynchrones fonctionnent différemment. Lorsqu'une demande arrive, ASP.NET utilise l'un de ses threads du pool et l'assigne à cette demande. Cette fois le gestionnaire de requêtes appellera cette ressource externe de façon asynchrone. Cela retourne le thread de demande pour le pool de threads jusqu'au retour de l'appel à la ressource externe. Figure 3 illustre le pool de threads avec deux fils, alors que la demande est en attente de façon asynchrone pour la ressource externe.

attente asynchrone d'une ressource externe
Figure 3 attente asynchrone d'une ressource externe

La différence importante est que le thread de demande est revenu au pool de threads lorsque l'appel asynchrone est en cours. Alors que le thread est dans le pool de threads, il est n'est plus associé à cette demande. Cette fois, quand l'appel de la ressource externe est retourné, ASP.NET utilise l'un de ses threads du pool et il réaffecte à cette demande. Ce thread continue le traitement de la demande. Lorsque la demande est terminée, retourné à nouveau thread dans le pool de threads. Notez qu'avec les gestionnaires synchrones, le même fil est utilisé pour la durée de vie de la demande ; avec gestionnaires asynchrones, en revanche, différents threads peuvent être affectés à la requête même (à des moments différents).

Maintenant, si les trois demandes étaient à venir, le serveur peut faire face facilement. Parce que les threads sont libérés au pool de threads, chaque fois que la demande a travail asynchrone, qu'il attend, ils sont libres de gérer les nouvelles demandes, ainsi que celles qui existent déjà. Requêtes asynchrones permettent un plus petit nombre de threads pour gérer un plus grand nombre de demandes. Le principal avantage du code asynchrone sur ASP.NET est donc l'évolutivité.

Pourquoi ne pas augmenter la taille de Pool de Thread ?

À ce stade, une question est toujours posée : Pourquoi ne pas simplement augmenter la taille du pool de threads ? La réponse est double : Code asynchrone les deux échelles plus loin et plus vite que le blocage des threads du pool.

Code asynchrone s'adapte plus loin que bloquer les threads car elle utilise beaucoup moins de mémoire ; chaque thread du pool sur un système d'exploitation moderne a une pile de 1MB, plus une pile de noyau unpageable. Qui ne ressemble pas beaucoup jusqu'à ce que vous commencez à obtenir tout un tas de fils sur votre serveur. En revanche, la mémoire de frais généraux pour une opération asynchrone est beaucoup plus petite. Ainsi, une requête avec une opération asynchrone a moindre sollicitation de la mémoire qu'une demande avec un thread bloqué. Code asynchrone vous permet d'utiliser votre mémoire pour d'autres choses (mise en cache, par exemple).

Code asynchrone s'adapte plus vite que le blocage des threads du pool de threads ayant un taux d'injection limitée. A ce jour, le taux est un thread toutes les deux secondes. Cette limite de taux d'injection est une bonne chose ; Il évite la destruction et construction constante de thread. Cependant, considérer ce qui se passe lors d'une crue soudaine des demandes. Code synchrone peut facilement s'enliser comme les requêtes utilisent tous les threads disponibles et les autres demandes doivent attendre pour le pool de threads d'injecter de nouvelles discussions. En revanche, le code asynchrone n'a pas besoin une limite comme ça ; C'est « always on », donc à prendre la parole. Code asynchrone est plus sensible aux fluctuations soudaines dans le volume de la demande.

Gardez à l'esprit que le code asynchrone ne remplace pas le pool de threads. Ce n'est pas de pool de threads ou de code asynchrone ; C'est de pool de threads et de code asynchrone. Code asynchrone permet à votre application à tirer le meilleur parti du pool de threads. Il prend le pool de threads existants et le transforme jusqu'à 11.

Qu'en est-il le fil faisant le travail asynchrone ?

Je me fais posé cette question tout le temps. L'implication est qu'il doit y avoir certains fil quelque part que s'il bloque l'appel I/O à la ressource externe. Alors, asynchrone code libère le thread de demande, mais seulement au détriment d'un autre thread ailleurs dans le système, droit ? Non, pas du tout.

Pour comprendre pourquoi les demandes asynchrones à l'échelle, je vais retracer un exemple (simplifié) d'un appel asynchrone de la I/O. Disons qu'une demande doit écrire dans un fichier. Le thread de demande appelle la méthode d'écriture asynchrone. WriteAsync est implémentée par la bibliothèque de classes de Base (BCL) et utilise les ports de terminaison pour les e/s asynchrones. Ainsi, l'appel WriteAsync est transmise au système d'exploitation comme une écriture de fichier asynchrone. Le système d'exploitation communique alors avec la pile de pilotes, en passant par les données à écrire dans un paquet de demande de I/O (IRP).

C'est où les choses deviennent intéressantes : Si un pilote de périphérique ne peut pas gérer un IRP immédiatement, il doit le gérer de façon asynchrone. Ainsi, le pilote raconte le disque pour commencer à écrire et renvoie une réponse « en attente » à l'OS. Le système d'exploitation passe que « dans l'attente de » réponse à la BCL et la BCL retourne une tâche inachevée à la demande-manipulation du code. Le code de gestion demande attend la tâche, qui retourne une tâche inachevée de cette méthode et ainsi de suite. Enfin, le code de gestion demande finit par regagner une tâche inachevée ASP.NET, et le thread de demande est libérée pour retourner au pool de threads.

Maintenant, examiner l'état actuel du système. Il existe différentes structures I/O qui ont été attribués (par exemple, les instances de la tâche et l'IRP) et ils sont tous dans un État en attente/incomplet. Cependant, il n'y a aucun thread qui est bloqué en attente pour qui écrire l'opération se termine. ASP.NET, ni la BCL, ni le système d'exploitation, ni le pilote de périphérique a un thread dédié au travail asynchrone.

Le disque écrit les données, il notifie son pilote via une interruption. Le pilote informe le système d'exploitation que l'IRP a terminé, et le système d'exploitation informe la BCL via le port de terminaison. Un thread du pool répond à cette notification en remplissant la tâche qui a été retournée par WriteAsync ; Il reprend à son tour le code demande asynchrone. Il y avait quelques fils « emprunté » pour des montants très courtes de temps au cours de cette phase d'achèvement-notification, mais aucun thread n'était effectivement bloqué alors que l'écriture était en cours.

Cet exemple est considérablement simplifié, mais il obtient partout au point principal : aucun thread n'est requis pour le vrai travail asynchrone. Aucun le temps CPU n'est nécessaire pour expulser effectivement les octets. Il y a aussi une leçon secondaire d'apprendre. Pensez au monde de pilote de périphérique, comment un pilote de périphérique doit soit gérer un IRP immédiatement ou de façon asynchrone. Traitement synchrone n'est pas une option. À l'échelle de pilote de périphérique, tous les e/sur le s non trivial est asynchrone. De nombreux développeurs ont un modèle mental qui traite de la « API naturel » pour les opérations d'e/s comme synchrone, avec l'API asynchrone comme une couche construite sur l'API naturel, synchrone. Cependant, c'est complètement vers l'arrière : en effet, l'API naturel est asynchrone ; et c'est l'API synchrones qui sont implémentées à l'aide d'e/s asynchrones !

Pourquoi n'étaient pas là gestionnaires asynchrones déjà ?

Si le traitement de la requête asynchrone est si merveilleux, pourquoi pas elle déjà disponible ? En fait, le code asynchrone est si bon pour l'évolutivité que la plate-forme ASP.NET a soutenu les gestionnaires asynchrones et les modules depuis les tout débuts de Microsoft .NET Framework. Pages Web asynchrones ont été introduites dans ASP.NET 2.0 et MVC obtenu contrôleurs asynchrones dans ASP.NET MVC 2.

Toutefois, jusqu'à tout récemment, code asynchrone a toujours été difficile à écrire et difficile à entretenir. Beaucoup de compagnies a décidé qu'il était plus facile tout autour de juste mettre le code de façon synchrone et payer pour les plus grandes fermes de serveurs ou d'hébergement plus cher. Maintenant, les rôles sont inversés : dans ASP.NET 4.5, asynchrone du code à l'aide d'async et attendent est presque aussi facile que d'écrire du code synchrone. Alors que grands systèmes entamons hébergement cloud et demande plus adapte, plus les entreprises adoptent async et vous attendent sur ASP.NET.

Code asynchrone n'est pas une solution miracle

Aussi merveilleux comme traitement de la requête asynchrone est, il ne sera pas résoudre tous vos problèmes. Il y a quelques malentendus communs autour de quel async et attendent peut faire sur ASP.NET.

Quand certains développeurs en savoir plus sur async et attendent, ils croient que c'est une façon pour le code serveur à « céder » au client (par exemple, le navigateur). Cependant, async et attendent sur le seul ASP.NET "rendement" du runtime ASP.NET ; le protocole HTTP reste inchangé, et vous avez toujours qu'une seule réponse par demande. Si vous aviez besoin de SignalR ou AJAX ou UpdatePanel avant async/attendent, vous aurez toujours besoin SignalR ou AJAX ou UpdatePanel après async/attendent.

Asynchrone demander traitement avec async et attendent peut aider votre échelle des applications. Cependant, c'est l'échelle sur un seul serveur ; vous devrez peut-être encore plan faire évoluer. Si vous avez besoin d'une architecture scale-out, vous aurez toujours besoin d'examiner sans État, requêtes idempotentes et fiable file d'attente. Async et attendent les faire un peu aider : ils vous permettent de profiter pleinement de vos ressources de serveur, donc vous n'aurez pas à évoluer aussi souvent. Mais si vous devez faire évoluer, vous aurez besoin d'une bonne architecture distribuée.

Async et vous attendent sur ASP.NET sont tous sur i/o. Vraiment, ils excellent dans la lecture et l'écriture des fichiers et les dossiers de base de données API REST. Toutefois, ils ne sont pas bonnes pour CPU-lié aux tâches. Vous pouvez le coup d'envoi un travail de fond en attendant Task.Run, mais il n'y a aucun point à cette fin. En fait, qui va réellement faire mal votre évolutivité en interférant avec l'heuristique de pool de thread ASP.NET . Si vous avez lié aux CPU de travail à faire sur ASP.NET, votre meilleur pari est de juste l'exécuter directement sur le thread de demande. En règle générale, ne file d'attente de travail pour le pool de threads sur ASP.NET.

Enfin, envisager l'évolutivité de votre système dans son ensemble. Il y a une dizaine d'années une architecture commune était d'avoir un serveur Web ASP.NET qui a parlé à un SQL Server de base de données back-end. Dans ce genre d'architecture simple, généralement le serveur de base de données est le goulot d'étranglement de l'évolutivité, pas le serveur Web. Rendre votre base de données des appels asynchrones aiderait probablement pas ; vous pourriez certainement les utiliser à l'échelle du serveur Web, mais le serveur de base de données empêche le système dans son ensemble de mise à l'échelle.

Rick Anderson fait valoir contre les appels de base de données asynchrones dans son excellent blog, « Mes appels de base de données doivent être asynchrones? » (bit.ly/1rw66UB). Il y a deux arguments qui soutiennent cela : tout d'abord, asynchrone est difficile (et donc cher dans la durée de développement par rapport aux juste d'acheter plus gros serveurs) ; et Deuxièmement, mise à l'échelle du serveur Web a peu de sens si l'extrémité arrière de la base de données est le goulot d'étranglement. Deux de ces arguments était parfaitement sensé quand ce message a été écrit, mais les deux arguments ont affaibli au fil du temps. Tout d'abord, asynchrone code est beaucoup plus facile d'écrire avec async et attendent. Deuxièmement, les données back-ends pour les sites Web sont l'échelle que le monde se déplace vers le cloud computing. Dos moderne se termine comme base de données SQL de Microsoft Azure, NoSQL et d'autres API s'adapte beaucoup plus loin qu'un simple SQL Server, repoussant le goulot d'étranglement sur le serveur Web. Dans ce scénario, async/attendent peut apporter un avantage énorme de mise à l'échelle ASP.NET.

Avant de commencer

La première chose que vous devez savoir est qu'async et attendent sont pris en charge uniquement sur ASP.NET 4.5. Il y a un package NuGet appelé Microsoft.Bcl.Async qui permet à async et attendent pour le .NET Framework 4, mais ne l'utilisez pas ; il ne fonctionnera pas correctement ! La raison est que ASP.NET lui-même a dû changer la façon dont il gère son maniement demande asynchrone pour mieux travailler avec async et attendent ; le package NuGet contient tous les types, le compilateur a besoin, mais n'est pas patch le runtime ASP.NET . Il n'y a aucune solution de contournement ; vous devez ASP.NET 4.5 ou version ultérieure.

Ensuite, sachez ASP.NET 4.5 introduit un mode « quirks » sur le serveur. Si vous créez un nouveau projet ASP.NET 4.5, vous n'avez pas à vous inquiéter. Toutefois, si vous mettez à niveau un projet existant à ASP.NET 4.5, les bizarreries sont tous allumés. Je recommande que vous les désactiver tout en éditant votre fichier web.config et affectant httpRuntime.targetFramework 4.5. Si votre application échoue avec ce paramètre (et vous ne voulez pas prendre le temps de le réparer), vous pouvez au moins obtenir async/attendent travaillant en ajoutant une clé appSetting d'aspnet:UseTaskFriendlySynchronizationContext avec la valeur "true". La clé appSetting n'est pas nécessaire si vous avez httpRuntime.targetFramework la valeur 4,5. L'équipe de développement Web a un blog sur les détails de cet nouvelle « mode quirks » à bit.ly/1pbmnzK. Conseil : Si vous voyez des comportements bizarres ou à des exceptions et que votre pile d'appels inclut LegacyAspNetSynchronizationContext, votre application s'exécute dans ce mode de bizarrerie. LegacyAspNet­SynchronizationContext n'est pas compatible avec async ; vous avez besoin de la AspNetSynchronizationContext régulière sur ASP.NET 4.5.

Dans ASP.NET 4.5, tous les paramètres de ASP.NET ont des valeurs de bon par défaut pour les demandes asynchrones, mais il y a quelques autres paramètres que vous pouvez modifier. Le premier est un paramètre IIS : envisager de relever la limite de file d'attente de IIS/HTTP.sys (Pools d'applications | Paramètres avancés | Longueur de la file d'attente) de sa valeur par défaut de 1 000 à 5 000. L'autre est un paramètre d'exécution .NET : ServicePointManager.DefaultConnectionLimit, qui a une valeur par défaut de 12 fois le nombre de cœurs. Le DefaultConnectionLimit limite le nombre de simultanément les connexions sortantes vers le même nom d'hôte.

Un mot sur l'abandon des requêtes

Lorsque ASP.NET traite une demande synchrone, il dispose d'un mécanisme très simple pour l'abandon d'une demande (par exemple, si la demande a dépassé son délai d'attente) : Il arrête le thread de travail pour cette demande. C'est logique dans le monde synchrone, où chaque demande a le même thread de travail de bout en bout. Abandon des discussions n'est pas merveilleux pour la stabilité à long terme de l'AppDomain, donc par défaut ASP.NET recyclera régulièrement votre application à nettoyer les choses.

Avec des requêtes asynchrones, ASP.NET ne sera pas abandon de threads de travail si elle veut abandonner une requête. Au contraire, il annulera la demande à l'aide d'un CancellationToken. Gestionnaires de demandes asynchrones doivent accepter et honorer les jetons d'annulation. La plupart des cadres plus récents (y compris les API Web, MVC et SignalR) construira et vous passer un CancellationToken directement ; tout ce que vous avez à faire est de le déclarer en tant que paramètre. Vous pouvez aussi accéder aux jetons ASP.NET directement ; par exemple, HttpRequest.TimedOutToken est un CancellationToken qui annule lorsque la demande arrive à expiration.

Alors que les applications entrons dans le nuage, abandon des requêtes devient plus important. Cloud Computing applications dépendent davantage de services externes qui peuvent prendre une quantité arbitraire de temps. Par exemple, un modèle standard est une demande externe avec exponentielle interruption ; Si votre application dépend de plusieurs services comme ceci, c'est une bonne idée d'appliquer un plafond de délai d'attente pour votre demande de traitement dans son ensemble.

État actuel de soutien Async

Beaucoup de bibliothèques ont été mis à jour pour la compatibilité avec async. Async support a été ajouté au Entity Framework (dans le package EntityFramework NuGet) dans la version 6. Il faut veiller à ne pas le chargement différé lorsque vous travaillez en mode asynchrone, cependant, car le chargement différé est toujours effectué de manière synchrone. HttpClient (dans le package Microsoft.Net.Http NuGet) est un client HTTP moderne conçu avec async en tête, idéal pour les appels externe reste API ; Il s'agit d'un remplacement modern pour HttpWebRequest et WebClient. La bibliothèque Client Microsoft Azure Storage (dans le package WindowsAzure.Storage NuGet) Ajout du support d'async dans la version 2.1.

Des cadres plus récents tels que Web API et SignalR ont un support complet pour une opération asynchrone et attendent. Web API, en particulier, a construit sa tuyauterie complète autour d'un soutien async : non seulement les contrôleurs async, mais async filtres et gestionnaires, trop. Web API et SignalR ont une histoire très naturel async : vous pouvez « just do it » et il « fonctionne, tout simplement. »

Cela nous amène à une histoire plus triste : Aujourd'hui, ASP.NET MVC que partiellement soutient async et attendent. Le support de base est là — actions de contrôleur async et annulation fonctionnent convenablement. Le site Web ASP.NET a un excellent tutoriel sur la façon d'utiliser les actions de contrôleur asynchrone ASP.NET MVC (bit.ly/1m1LXTx) ; C'est la meilleure ressource pour débuter avec async sur MVC. Malheureusement, le MVC ASP.NET n'est pas (actuellement) prend en charge les filtres async (bit.ly/1oAyHLc) ou les actions d'enfant async (bit.ly/1px47RG).

ASP.NET Web Forms est un framework plus âgé, mais elle aussi a un soutien adéquat pour une opération asynchrone et attendent. Encore une fois, la meilleure ressource pour la mise en route est le tutoriel sur le site Web ASP.NET pour une opération asynchrone Web Forms (bit.ly/Ydho7W). Avec les Web Forms, soutien async est opt-in. Vous devez d'abord définir Page.Async sur true, vous pouvez alors utiliser PageAsyncTask pour enregistrer async travail avec cette page (vous pouvez également utiliser les gestionnaires d'événement Sub async). PageAsyncTask prend également en charge d'annulation.

Si vous avez un gestionnaire HTTP personnalisé ou un module HTTP, ASP.NET prend désormais en charge les versions asynchrones de ceux, aussi bien. Les gestionnaires HTTP sont pris en charge par l'intermédiaire de HttpTaskAsyncHandler (bit.ly/1nWpWFj) et les modules HTTP sont pris en charge par l'intermédiaire de EventHandlerTaskAsyncHelper (bit.ly/1m1Sn4O).

A ce jour, l'équipe de ASP.NET travaille sur un nouveau projet appelé vNext ASP.NET . VNext, la tuyauterie complète est asynchrone par défaut. Actuellement, le plan consiste à combiner le MVC et Web API dans un même cadre qui possède un support complet pour async/attendent (y compris les filtres async et async Voir composants). Autres cadres d'async-prêt comme SignalR vont trouverez un accueil naturel vNext. Vraiment, le futur est asynchrone.

Respecter les filets de sécurité

ASP.NET 4.5 introduit quelques nouvelles "filets de sécurité" qui vous aider à attraper les problèmes asynchrones dans votre application. Ceux-ci sont sur par défaut et doivent rester allumé.

Lorsqu'un gestionnaire synchrone tente d'effectuer le travail asynchrone, vous obtiendrez une exception InvalidOperationException avec le message, « impossible de démarrer une opération asynchrone en ce moment. » Il y a deux causes principales de cette exception. Le premier est quand une page Web Forms a des gestionnaires d'événements asynchrones, mais négligées à Page.Async la valeur true. Le second est lorsque le code synchrone appelle une méthode void async. C'est encore une autre raison d'éviter async vide.

L'autre filet de sécurité est pour gestionnaires asynchrones : Quand un gestionnaire asynchrone termine à la demande, mais ASP.NET détecte travail asynchrone qui n'a pas terminé, vous obtenez un invalide­OperationException avec le message, "un module asynchrone ou gestionnaire terminé alors qu'une opération asynchrone était encore en suspens. » Il s'agit généralement en raison de code asynchrone en appelant une méthode void async, mais elle peut aussi être causée par une mauvaise utilisation d'un composant de modèle asynchrone basé sur des événements (EAP) (bit.ly/19VdUWu).

Il y a une option, que vous pouvez utiliser pour désactiver les deux filets de sécurité : HttpContext.AllowAsyncDuringSyncStages (il peut également être définie dans le fichier web.config). Quelques pages sur Internet suggèrent que ce réglage quand vous voyez ces exceptions. Je ne peux pas être en désaccord avec plus de véhémence. Sérieusement, je ne sais pas pourquoi, c'est encore possible. Désactiver les filets de sécurité, est une idée horrible. La seule raison possible que je peux penser est si votre code est déjà faire quelques très avancée des trucs asynchrone (au-delà de tout ce que j'ai jamais essayé), et vous êtes un génie de multithreading. Donc, si vous avez lu cet article en entier le bâillement et la pensée, « s'il vous plaît, je ne suis aucun n00b, » alors je suppose que vous pouvez désactiver les filets de sécurité. Pour le reste d'entre nous, c'est une option extrêmement dangereuse et ne doit pas être définie sauf si vous êtes pleinement conscient des ramifications.

démarrage

Enfin ! Prêt à démarrer en profitant d'async et attendent ? J'apprécie votre patience.

Tout d'abord, consultez la section « Asynchrone Code est pas un Silver Bullet » dans cet article pour async/attendent soit bénéfique à votre architecture. Ensuite, mettez à jour votre application ASP.NET 4,5 et désactiver le mode quirks (il n'est pas une mauvaise idée pour l'exécuter à ce point juste pour s'assurer que rien ne se casse). À ce stade, vous êtes prêt à commencer les travaux de vrai async/attendent.

Commencer aux « feuilles ». Pensez à comment vos demandes sont traitées et identifient toute opérations I/O-basée, surtout n'importe quoi sur le réseau. Des exemples courants sont les requêtes de base de données et des commandes et des appels à d'autres services Web et des API. Commencer par choisir l'un et faire un peu de recherche pour trouver la meilleure option pour effectuer cette opération en utilisant async/attendent. Bon nombre de types prédéfinis BCL sont maintenant prêts pour async dans le .NET Framework 4.5 ; par exemple, SmtpClient dispose des méthodes de SendMailAsync. Certains types sont prêts async remplacements disponibles ; par exemple, HttpWebRequest et WebClient peuvent être remplacé avec HttpClient. Mise à jour de vos versions de bibliothèques, si nécessaire ; par exemple, Entity Framework obtenu méthodes async-compatible dans EF6.

Toutefois, éviter les « faux asynchronie » dans les bibliothèques. Faux asynchronisme est quand un composant a une API asynchrone-prêt, mais il est mis en œuvre en enroulant juste l'API synchrone dans un thread du pool. C'est contreproductif d'évolutivité sur ASP.NET. Un exemple de faux asynchronisme est Newtonsoft JSON.NET, une bibliothèque par ailleurs excellente. Il est préférable de ne pas appeler les versions asynchrones (fausses) pour la sérialisation JSON ; appelez simplement les versions synchrones. Un exemple plus compliqué d'asynchronie faux est les flux de fichiers BCL. Lorsqu'un fichier est ouvert, il doit être explicitement ouvert pour l'accès asynchrone ; dans le cas contraire, il utilisera l'asynchronie faux, synchrone bloque un thread du pool sur le fichier lit et écrit.

Une fois que vous avez choisi une "feuille", puis commencez par une méthode dans votre code qui appelle cette API et en faire une méthode asynchrone qui appelle l'API asynchrone-prêt via await. Si l'API que vous appellerez supporte CancellationToken, votre méthode doit prendre un CancellationToken et le transmettre à la méthode de l'API.

Chaque fois que vous marquez une méthode asynchrone, vous devez modifier son type de retour : Sub devient opérationnelle, et un type non void T devient tâche < T >. Vous verrez qu'ensuite tous les appelants de cette méthode doivent devenir asynchrone afin qu'ils peuvent attendre la tâche et ainsi de suite. En outre, ajouter Async au nom de votre méthode, pour suivre les conventions du modèle asynchrone basé sur les tâches (bit.ly/1uBKGKR).

Laissez le motif async/attendent de grandir votre pile d'appels vers le « tronc ». Au tronc, votre code sera en interface avec le ASP.NET framework (MVC, formulaires Web, API Web). Lire le tutoriel approprié dans la section « État actuel de l'appui Async » plus haut dans cet article pour intégrer votre code asynchrone avec votre cadre.

Le long du chemin, identifier n'importe quel état local de thread. Car les demandes asynchrones peuvent changer des threads, état local de thread comme ThreadStaticAttribute, ThreadLocal < T >, les emplacements de données thread et les CallContext.GetData/SetData ne fonctionnera pas. Remplacer par HttpContext.Items, si possible ; ou vous pouvez stocker des données immuables dans CallContext.LogicalGetData/LogicalSetData.

Voici une astuce que j'ai trouvé utile : vous pouvez dupliquer (temporairement) votre code pour créer une partition verticale. Avec cette technique, vous ne changez pas vos méthodes synchrones à asynchrone ; vous copiez l'ensemble de la méthode synchrone et puis modifiez la copie pour être asynchrone. Vous pouvez alors garder la plupart de votre application en utilisant les méthodes synchrones et il suffit de créer une petite tranche verticale d'asynchronie. C'est super si vous voulez explorer async comme une preuve de concept ou essai sur une partie de la demande pour avoir une idée de comment votre système pourrait évoluer de chargement. Vous pouvez avoir une demande (ou page) qui est complètement asynchrone, tandis que le reste de votre application reste synchrone. Bien sûr, vous ne souhaitez pas conserver les doublons pour chacune de vos méthodes ; Finalement, tout le code d'e/S sera async et les copies synchrones peuvent être enlevés.

Synthèse

J'espère que cet article vous a aidé à obtenir une base conceptuelle dans des requêtes asynchrones sur ASP.NET. À l'aide d'async et attendent, il est plus facile que jamais d'écrire des applications Web, services et API qui font le maximum l'utilisation de leurs ressources de serveur. Async est génial !


Stephen Cleary est un mari, le père et le programmeur vivant dans le nord du Michigan. Il a travaillé avec multithreading et asynchrones programmation pendant 16 ans et a utilisé le soutien async dans Microsoft .NET Framework depuis la première preview de technologie de communauté. Sa page d'accueil, y compris son blog, est à stephencleary.com.

Merci à l'expert technique Microsoft suivant d'avoir relu cet article : James McCaffrey