Les méthodes asynchrones D'ApiController-Quel est le profit? Quand les utiliser?

(ce qui fait probablement double emploi avec la question ) ASP.NET MVC4 async controller-Pourquoi utiliser? , mais à propos de webapi, et je ne suis pas d'accord avec les réponses là-dedans)

Suppose que j'ai une requête SQL de longue durée. Ses données devraient être sérialisées à JSON et envoyées au navigateur (en réponse à la demande xhr). Code de l'échantillon:

public class DataController : ApiController
{
    public Task<Data> Get()
    {
        return LoadDataAsync(); // Load data asynchronously?
    }
}

Ce qui se passe réellement quand je fais $.getJson ('api/data',...) (voir cette affiche http://www.asp.net/posters/web-api/ASP.NET-Web-API-Poster.pdf ):

  1. [IIS] la Demande est acceptée par le serveur IIS.
  2. [IIS] IIS attend un fil [fil] de la piscine gérée ( http://msdn.microsoft.com/en-us/library/0ka9477y (v=V110).aspx ) et commence à y travailler.
  3. [THREAD] Webapi crée un nouvel objet DataController dans ce thread, et dans d'autres classes.
  4. [THREAD] Utilise des tâches parallèles lib pour démarrer sql-query dans [THREAD2]
  5. [THREAD] remonte pour la gestion de la piscine, prêt pour un autre traitement
  6. [THREAD2] fonctionne avec le pilote sql, lit les données comme il est prêt et invoque [THREAD3] pour répondre à la demande xhr
  7. [THREAD3] envoie la réponse.

s'il vous plaît, n'hésitez pas à me corriger, s'il y a quelque chose qui ne va pas.

dans le question ci-dessus, ils disent, le point et le profit est, que [THREAD2] n'est pas de la piscine gérée, mais L'article MSDN (lien ci-dessus) dit que

par défaut, les types de bibliothèques parallèles comme Task et Task<TResult> utilisent des threads de thread pool pour exécuter des tâches.

donc je fais une conclusion, que les trois fils sont de managed pool.

de plus, si j'utilisais la méthode synchrone, je garderais toujours mon répondant au serveur, en utilisant un seul thread (du précieux pool de threads).

alors, à quoi sert le fait de passer d'un fil à trois fils? Pourquoi ne pas simplement maximiser les threads dans la piscine de fil?

Existe-t-il des moyens clairement utiles d'utiliser les contrôleurs async?

25
demandé sur Community 2013-11-12 20:23:21

4 réponses

je pense que la principale incompréhension concerne la façon dont les tâches async fonctionnent. J'ai un async intro sur mon blog qui pourrait vous aider.

en particulier, un Task retourné par une méthode async n'exécute aucun code. Plutôt, c'est juste un moyen pratique pour informer les appelants du résultat de cette méthode. Les docs MSDN que vous avez cités ne s'appliquent qu'aux tâches qui exécutent effectivement du code, par exemple Task.Run .

BTW, the l'affiche que vous avez mentionnée n'a rien à voir avec les fils. Voici ce qui se passe dans une demande de base de données async (légèrement simplifiée):

  1. la demande est acceptée par IIS et transmise à ASP.NET.
  2. ASP.NET prend un de ses threads de thread pool et l'assigne à cette requête.
  3. WebApi crée DataController etc.
  4. l'action du contrôleur lance une requête SQL asynchrone.
  5. le thread de requête retourne dans le pool de threads. Il n'y a plus de threads pour traiter la requête.
  6. lorsque le résultat arrive du serveur SQL, un thread pool lit la réponse.
  7. que thread pool thread avise la demande qu'elle est prête à poursuivre le traitement.
  8. depuis ASP.NET sait qu'aucun autre thread ne gère cette requête, il assigne juste le même thread à la requête pour qu'elle puisse la terminer éteint directement.

Si vous en voulez la preuve-de-concept de code, j'ai Gist artificiellement restreint l'ASP.NET pool de threads pour le nombre de cœurs (qui est à son minimum) et ne N+1 demandes synchrones et asynchrones. Ce code ne fait qu'un retard d'une seconde au lieu de contacter un serveur SQL, mais le principe général est le même.

29
répondu Stephen Cleary 2013-11-12 19:15:20

le profit des actions asynchrones est que pendant que le contrôleur attend que la requête sql termine aucun threads ne sont alloués pour cette requête, tandis que si vous utilisez une méthode synchrone un thread serait verrouillé dans l'exécution de cette méthode du début à la fin de cette méthode. Pendant que SQL server fait son travail, le thread ne fait rien d'autre que d'attendre. Si vous utilisez des méthodes asynchrones, ce même thread peut répondre à d'autres requêtes pendant que SQL server fait son truc.

je crois que vos pas sont mauvais à l'étape 4, Je ne pense pas qu'il va créer un nouveau fil pour faire la requête SQL. A 6 il n'y a pas de nouveau thread créé, c'est juste un des threads disponibles qui sera utilisé pour continuer là où le premier thread s'est arrêté. Le fil à 6 pourrait être le même que commencé l'opération asynchrone.

4
répondu Olav Nybø 2013-11-12 18:27:11

j'ai mon avis la suivante décrit un avantage évident de contrôleurs async plus synchrone.

une application web utilisant des méthodes synchrones pour servir une haute latence appels lorsque le pool de threads atteint le maximum par défaut de .net 4.5 5 000 threads consommeraient environ 5 Go de mémoire de plus qu'un les mêmes demandes en utilisant asynchrone méthodes et seulement 50 threads. Quand vous faites asynchrone travail, vous n'êtes pas toujours à l'aide d'un fil. Par exemple, lorsque vous effectuez un demande de service Web asynchrone, ASP.NET ne pas utiliser les fils entre l'appel de méthode async et l'attente. En utilisant le fil les requêtes pool to service avec une latence élevée peuvent conduire à une grande mémoire encombrement et mauvaise utilisation du matériel du serveur.

from Using Asynchronous Methods in ASP.NET MVC 4

3
répondu Major Byte 2013-11-12 18:36:54

le but d'async n'est pas de faire une application multi threadée mais plutôt de laisser une application filetée simple continuer avec quelque chose de différent au lieu d'attendre une réponse d'un appel externe qui exécute sur un fil ou un processus différent.

envisager une application de bureau qui montre les prix des actions de différentes bourses. L'application doit faire quelques appels REST / http pour obtenir des données de chacune des bourses distantes. serveur.

une application filetée simple ferait le premier appel, attendre de ne rien faire jusqu'à ce qu'il ait obtenu la première série de prix, mettre à jour sa fenêtre, puis faire un appel au serveur de cours d'actions externes suivant, encore attendre de ne rien faire jusqu'à ce qu'il ait obtenu les prix, mettre à jour sa fenêtre... etc..

nous pourrions aller tous multi threaded lancer les requêtes en parallèle et mettre à jour l'écran en parallèle, mais comme la plupart du temps est passé à attendre une réponse de le serveur distant cela semble exagéré.

il pourrait être préférable pour le fil à: Faire une demande pour la première serveur, mais au lieu d'attendre la réponse laisser un repère, un lieu de revenir lorsque le prix à l'arrivée et de passer à l'émission de la deuxième demande, à nouveau en laissant un marqueur d'un lieu de revenir...etc.

lorsque toutes les requêtes ont été émises, le thread d'exécution de l'application peut continuer à traiter les entrées de l'utilisateur ou ce qui est requis.

maintenant, lorsqu'une réponse d'un des serveurs est reçue, le thread peut être dirigé à partir du marqueur qu'il a posé précédemment et mettre à jour la fenêtre.

Tout ce qui précède aurait pu être codé Main Longue, Simple fileté, mais était si horrible aller multi filetage était souvent plus facile. Maintenant le processus de quitter le marqueur et de revenir est fait par le compilateur lorsque nous écrivons async/wait. Tous les mono-thread.

il y a deux points clés ici:

1) Multi threaded ne se produisent encore! Le traitement de notre demande de cours des actions se fait sur un fil différent (sur une machine différente). Si nous faisions l'accès db la même chose serait vraie. Dans les exemples où l'attente est pour une minuterie, la minuterie fonctionne sur un thread différent. Notre application est filetée simple, le point d'exécution saute juste autour (d'une manière contrôlée) tandis que les threads externes exécutent

2) Nous perdre le bénéfice de l'exécution asynchrone dès que l'application nécessite une opération asynchrone à compléter. Considérons une application montrant le prix du café de deux bourses, l'application pourrait initier les demandes et mettre à jour ses fenêtres de manière asynchrone sur un seul fil, mais maintenant, si l'application a également calculé la différence de prix entre deux bourses, il faudrait attendre les appels asynchrones à compléter. Cela nous est imposé parce qu'une méthode asynchrone (telle qu'une nous pourrions écrire pour appeler une bourse pour un cours des actions) ne renvoie pas le cours des actions, mais une tâche, qui peut être considérée comme un moyen de revenir au marqueur qui a été fixé de sorte que la fonction peut compléter et retourner le cours des actions.

cela signifie que chaque fonction qui appelle une fonction async doit être async ou attendre l'appel" autre thread/process/machine " en bas de la pile d'appels pour terminer, et si nous attendons que l'appel en bas se termine bien pourquoi s'embêter avec async?

lorsque l'écriture d'une api web, IIS ou un autre hôte est l'application de bureau, nous écrivons nos méthodes de contrôleur async de sorte que l'hôte peut exécuter d'autres méthodes sur notre thread pour répondre à d'autres demandes alors que notre code est en attente d'une réponse de travail sur un thread/processus/machine différent.

3
répondu Jamie_M_ 2014-10-09 13:47:16