Effectuer des opérations asynchrones dans ASP.NET MVC utiliser un thread de ThreadPool sur .NET 4

158

Après cette question, cela me met à l'aise lors de l'utilisation d'opérations asynchrones dans ASP.NET MVC. J'ai donc écrit deux articles de blog à ce sujet:

J'ai trop de malentendus dans mon esprit sur les opérations asynchrones sur ASP.NET MVC.

J'entends toujours cette phrase: l' application peut mieux évoluer si les opérations s'exécutent de manière asynchrone

Et j'ai aussi beaucoup entendu ce genre de phrases: si vous avez un volume de trafic énorme, vous feriez peut-être mieux de ne pas effectuer vos requêtes de manière asynchrone - consommer 2 threads supplémentaires pour traiter une demande enlève des ressources aux autres demandes entrantes.

Je pense que ces deux phrases sont incohérentes.

Je n'ai pas beaucoup d'informations sur le fonctionnement de threadpool sur ASP.NET mais je sais que threadpool a une taille limitée pour les threads. Donc, la deuxième phrase doit être liée à cette question.

Et je voudrais savoir si les opérations asynchrones dans ASP.NET MVC utilisent un thread de ThreadPool sur .NET 4?

Par exemple, lorsque nous implémentons un AsyncController, comment se structure l'application? Si j'obtiens un trafic énorme, est-ce une bonne idée d'implémenter AsyncController?

Y a-t-il quelqu'un là-bas qui peut enlever ce rideau noir sous mes yeux et m'expliquer l'accord sur l'asynchronie sur ASP.NET MVC 3 (NET 4)?

Éditer:

J'ai lu ce document ci-dessous près de centaines de fois et je comprends le problème principal, mais j'ai toujours de la confusion car il y a trop de commentaires incohérents.

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

Éditer:

Supposons que j'ai une action de contrôleur comme ci-dessous (pas une implémentation de AsyncControllercependant):

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Do an advanced looging here which takes a while
    });

    return View();
}

Comme vous le voyez ici, je lance une opération et l'oublie. Ensuite, je rentre tout de suite sans attendre qu'il soit terminé.

Dans ce cas, cela doit-il utiliser un thread de threadpool? Si tel est le cas, une fois terminé, qu'advient-il de ce fil? Est-ce qu'il GCentre et nettoie juste après la fin?

Éditer:

Pour la réponse de @ Darin, voici un exemple de code asynchrone qui communique avec la base de données:

public class FooController : AsyncController {

    //EF 4.2 DbContext instance
    MyContext _context = new MyContext();

    public void IndexAsync() { 

        AsyncManager.OutstandingOperations.Increment(3);

        Task<IEnumerable<Foo>>.Factory.StartNew(() => { 

           return 
                _context.Foos;
        }).ContinueWith(t => {

            AsyncManager.Parameters["foos"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });

        Task<IEnumerable<Bars>>.Factory.StartNew(() => { 

           return 
                _context.Bars;
        }).ContinueWith(t => {

            AsyncManager.Parameters["bars"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });

        Task<IEnumerable<FooBar>>.Factory.StartNew(() => { 

           return 
                _context.FooBars;
        }).ContinueWith(t => {

            AsyncManager.Parameters["foobars"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });
    }

    public ViewResult IndexCompleted(
        IEnumerable<Foo> foos, 
        IEnumerable<Bar> bars,
        IEnumerable<FooBar> foobars) {

        //Do the regular stuff and return

    }
}
tugberk
la source
Je ne suis pas sûr de la réponse, mais il convient de noter que l'asynchrone et le multi-threading sont des choses différentes. Il serait donc possible d'avoir un nombre fixe de threads avec une gestion asynchrone. Ce qui se passerait, c'est lorsqu'une page doit bloquer pour, par exemple, les E / S, une autre page aurait la possibilité de s'exécuter sur le même thread. C'est ainsi que ces deux déclarations peuvent être vraies, async peut rendre les choses plus rapides mais trop de threads est un problème.
Chris Chilvers
@ChrisChilvers Oui, le multithreading n'est pas toujours nécessaire en fonctionnement asynchrone. Je l'ai déjà compris mais je pense que je n'ai aucun contrôleur à ce sujet pour autant que je sache. AsyncController fait tourner le nombre de threads qu'il veut de mon point de vue mais pas sûr non plus. Existe-t-il également une notion de threadpool sur les applications de bureau comme WPF? Je pense que le nombre de threads n'est pas un problème sur ce type d'applications.
tugberk
6
Regardez la vidéo Threading avec Jeff Richter
oleksii
Je pense que le problème (et donc l'incohérence) est que la deuxième déclaration utilise asynchrone quand cela signifie de nombreux threads. Cela pourrait être dû au fait que c'est ainsi qu'asp.net a implémenté des pages asynchrones et que l'implémentation spécifique a donc confondu le problème (puisque le nom de la fonctionnalité à l'origine du problème serait des pages asynchrones), mais je ne suis pas sûr de l'implémentation spécifique . Donc, soit ils signifient «beaucoup de threads», soit ils signifient «pages asynchrones dans asp.net version X» puisque les futures versions peuvent changer l'implémentation. Ou ils veulent simplement dire utiliser le pool de threads pour effectuer une asynchrone dans une page.
Chris Chilvers
@ChrisChilvers oh, mec! Je suis plus confus après ces commentaires: s
tugberk

Réponses:

177

Voici un excellent article que je vous recommanderais de lire pour mieux comprendre le traitement asynchrone dans ASP.NET (ce que représentent essentiellement les contrôleurs asynchrones).

Considérons d'abord une action synchrone standard:

public ActionResult Index()
{
    // some processing
    return View();
}

Lorsqu'une demande est faite à cette action, un thread est tiré du pool de threads et le corps de cette action est exécuté sur ce thread. Donc, si le traitement à l'intérieur de cette action est lent, vous bloquez ce thread pendant tout le traitement, ce thread ne peut donc pas être réutilisé pour traiter d'autres requêtes. À la fin de l'exécution de la requête, le thread est renvoyé au pool de threads.

Prenons maintenant un exemple du modèle asynchrone:

public void IndexAsync()
{
    // perform some processing
}

public ActionResult IndexCompleted(object result)
{
    return View();
}

Lorsqu'une requête est envoyée à l'action Index, un thread est tiré du pool de threads et le corps de la IndexAsyncméthode est exécuté. Une fois l'exécution du corps de cette méthode terminée, le thread est renvoyé dans le pool de threads. Ensuite, en utilisant la norme AsyncManager.OutstandingOperations, une fois que vous avez signalé la fin de l'opération asynchrone, un autre thread est tiré du pool de threads et le corps de l' IndexCompletedaction est exécuté sur celui-ci et le résultat est rendu au client.

Donc, ce que nous pouvons voir dans ce modèle, c'est qu'une seule requête HTTP client pourrait être exécutée par deux threads différents.

Maintenant, la partie intéressante se passe à l'intérieur de la IndexAsyncméthode. Si vous avez une opération de blocage à l'intérieur, vous gaspillez totalement le but des contrôleurs asynchrones parce que vous bloquez le thread de travail (rappelez-vous que le corps de cette action est exécuté sur un thread tiré du pool de threads).

Alors, quand pouvons-nous vraiment tirer parti des contrôleurs asynchrones que vous pourriez demander?

À mon humble avis, nous pouvons gagner le plus lorsque nous avons des opérations intensives d'E / S (telles que la base de données et les appels réseau vers des services distants). Si vous avez une opération intensive en CPU, les actions asynchrones ne vous apporteront pas beaucoup d'avantages.

Alors, pourquoi pouvons-nous tirer parti des opérations intensives d'E / S? Parce que nous pourrions utiliser les ports d' achèvement d' E / S . Les IOCP sont extrêmement puissants car vous ne consommez pas de threads ou de ressources sur le serveur pendant l'exécution de toute l'opération.

Comment travaillent-ils?

Supposons que nous souhaitons télécharger le contenu d'une page Web distante à l'aide de la méthode WebClient.DownloadStringAsync . Vous appelez cette méthode qui enregistrera un IOCP dans le système d'exploitation et reviendra immédiatement. Pendant le traitement de l'ensemble de la requête, aucun thread n'est consommé sur votre serveur. Tout se passe sur le serveur distant. Cela peut prendre beaucoup de temps, mais cela ne vous dérange pas car vous ne mettez pas en péril vos threads de travail. Une fois qu'une réponse est reçue, l'IOCP est signalé, un thread est tiré du pool de threads et le rappel est exécuté sur ce thread. Mais comme vous pouvez le voir, pendant tout le processus, nous n'avons monopolisé aucun thread.

Il en va de même avec des méthodes telles que FileStream.BeginRead, SqlCommand.BeginExecute, ...

Qu'en est-il de la parallélisation de plusieurs appels de base de données? Supposons que vous ayez une action de contrôleur synchrone dans laquelle vous avez effectué 4 appels de base de données bloquants en séquence. Il est facile de calculer que si chaque appel de base de données prend 200 ms, l'action de votre contrôleur prendra environ 800 ms pour s'exécuter.

Si vous n'avez pas besoin d'exécuter ces appels de manière séquentielle, leur parallélisation améliorerait-elle les performances?

C'est la grande question à laquelle il n'est pas facile de répondre. Peut-être que oui, peut-être que non. Cela dépendra entièrement de la façon dont vous implémentez ces appels de base de données. Si vous utilisez des contrôleurs asynchrones et des ports d'achèvement d'E / S comme indiqué précédemment, vous améliorerez les performances de cette action de contrôleur et d'autres actions, car vous ne monopoliserez pas les threads de travail.

En revanche, si vous les implémentez mal (avec un appel de base de données bloquant effectué sur un thread du pool de threads), vous réduirez fondamentalement le temps total d'exécution de cette action à environ 200 ms mais vous auriez consommé 4 threads de travail donc vous peut avoir dégradé les performances d'autres requêtes qui peuvent devenir affamées en raison de threads manquants dans le pool pour les traiter.

C'est donc très difficile et si vous ne vous sentez pas prêt à effectuer des tests approfondis sur votre application, n'implémentez pas de contrôleurs asynchrones, car il y a de fortes chances que vous fassiez plus de dégâts que de bénéfices. Ne les implémentez que si vous avez une raison de le faire: par exemple, vous avez identifié que les actions standard du contrôleur synchrone constituent un goulot d'étranglement pour votre application (après avoir effectué des tests de charge et des mesures approfondis bien sûr).

Considérons maintenant votre exemple:

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Do an advanced looging here which takes a while
    });

    return View();
}

Lorsqu'une demande est reçue pour l'action Index, un thread est tiré du pool de threads pour exécuter son corps, mais son corps ne planifie qu'une nouvelle tâche à l'aide de TPL . Ainsi, l'exécution de l'action se termine et le thread est renvoyé dans le pool de threads. Sauf que, TPL utilise des threads du pool de threads pour effectuer leur traitement. Ainsi, même si le thread d'origine a été renvoyé dans le pool de threads, vous avez dessiné un autre thread de ce pool pour exécuter le corps de la tâche. Vous avez donc mis en péril 2 threads de votre précieux pool.

Considérons maintenant ce qui suit:

public ViewResult Index() { 

    new Thread(() => { 
        //Do an advanced looging here which takes a while
    }).Start();

    return View();
}

Dans ce cas, nous créons manuellement un thread. Dans ce cas, l'exécution du corps de l'action Index peut prendre un peu plus de temps (car générer un nouveau thread est plus coûteux que d'en tirer un à partir d'un pool existant). Mais l'exécution de l'opération de journalisation avancée se fera sur un thread qui ne fait pas partie du pool. Nous ne mettons donc pas en péril les threads du pool qui restent libres pour répondre à une autre requête.

Darin Dimitrov
la source
1
Vraiment détaillé, merci! Supposons que nous avons 4 tâches asynchrones ( System.Threading.Task) exécutées dans la IndexAsyncméthode. À l'intérieur de ces opérations, nous faisons des appels db à un serveur. Donc, toutes sont des opérations intensives d'E / S, non? Dans ce cas, créons-nous 4 threads séparés (ou obtient-on 4 threads séparés du pool de threads)? En supposant que j'ai une machine multicœur, ils vont également fonctionner en parallèle, non?
tugberk
10
@tugberk, les appels de base de données sont des opérations d'E / S, mais tout dépendra de la façon dont vous les implémentez. Si vous utilisez un appel de base de données bloquant tel que SqlCommand.ExecuteReadervous gaspillez tout car il s'agit d'un appel bloquant. Vous bloquez le thread sur lequel cet appel s'exécute et si ce thread se trouve être un thread du pool, c'est très mauvais. Vous bénéficierez uniquement si vous utilisez E / S Ports d' achèvement: SqlCommand.BeginExecuteReader. Si vous n'utilisez pas IOCP quoi que vous fassiez, n'utilisez pas de contrôleurs asynchrones car vous ferez plus de dégâts que de bénéfices sur les performances globales de votre application.
Darin Dimitrov le
1
Eh bien, la plupart du temps, j'utilise d'abord le code EF. Je ne sais pas si cela convient. J'ai mis en place un échantillon qui montre ce que je fais généralement. J'ai mis à jour la question, pouvez-vous jeter un œil?
tugberk
3
@tugberk, vous les exécutez en parallèle, de sorte que le temps total d'exécution est moindre que si vous les exécutez séquentiellement. Mais pour les exécuter, vous utilisez des threads de travail. En fait, EF est paresseux, alors quand vous le faites, _context.Foovous n'exécutez rien. Vous construisez juste un arbre d'expression. Soyez extrêmement prudent avec cela. L'exécution de la requête est différée uniquement lorsque vous commencez à énumérer le jeu de résultats. Et si cela se produit dans la vue, cela peut être catastrophique pour les performances. Pour exécuter avec empressement une requête EF, ajoutez .ToList()à la fin.
Darin Dimitrov le
2
@tugberk, vous aurez besoin d'un outil de test de charge afin de simuler plusieurs utilisateurs en parallèle sur votre site Web pour voir comment il se comporte sous une charge importante. Mini Profiler ne peut pas simuler la charge sur votre site Web. Cela peut vous aider à voir et à optimiser vos requêtes ADO.NET et à profiler une seule requête, ce qui est inutile lorsque vous avez besoin de voir comment votre site se comporte dans une situation réelle lorsque de nombreux utilisateurs le consultent.
Darin Dimitrov le
49

Oui - tous les threads proviennent du pool de threads. Votre application MVC est déjà multithread, lorsqu'une demande arrive dans un nouveau thread sera extrait du pool et utilisé pour traiter la demande. Ce thread sera «verrouillé» (à partir d'autres demandes) jusqu'à ce que la demande soit entièrement traitée et terminée. S'il n'y a pas de thread disponible dans le pool, la demande devra attendre jusqu'à ce qu'il soit disponible.

Si vous avez des contrôleurs asynchrones, ils obtiennent toujours un thread du pool mais tout en traitant la demande, ils peuvent abandonner le thread, en attendant que quelque chose se passe (et ce thread peut être donné à une autre demande) et lorsque la demande d'origine a besoin d'un thread encore une fois, il en obtient un de la piscine.

La différence est que si vous avez beaucoup de requêtes de longue durée (où le thread attend une réponse de quelque chose), vous risquez de manquer de threads du pool pour répondre même aux requêtes de base. Si vous avez des contrôleurs asynchrones, vous n'avez plus de threads mais les threads en attente sont renvoyés dans le pool et peuvent traiter d'autres requêtes.

Un exemple presque réel ... Pensez-y comme si vous montiez dans un bus, il y a cinq personnes qui attendent pour monter, le premier monte, paie et s'assoit (le chauffeur a répondu à sa demande), vous montez (le chauffeur fait l'entretien votre demande) mais vous ne trouvez pas votre argent; lorsque vous fouillez dans vos poches, le chauffeur vous abandonne et engage les deux personnes suivantes (répondant à leurs demandes), lorsque vous trouvez votre argent, le chauffeur recommence à traiter avec vous (complétant votre demande) - la cinquième personne doit attendre jusqu'à vous avez terminé, mais les troisième et quatrième personnes ont été servies alors que vous en étiez à mi-chemin. Cela signifie que le conducteur est le seul et unique fil conducteur de la piscine et les passagers sont les demandes. C'était trop compliqué d'écrire comment cela fonctionnerait s'il y avait deux pilotes mais vous pouvez imaginer ...

Sans un contrôleur asynchrone, les passagers derrière vous devraient attendre longtemps pendant que vous cherchez votre argent, tandis que le chauffeur de bus ne ferait aucun travail.

Donc, la conclusion est que si beaucoup de gens ne savent pas où se trouve leur argent (c'est-à-dire qu'ils ont besoin de beaucoup de temps pour répondre à quelque chose que le pilote a demandé), les contrôleurs asynchrones pourraient bien aider à traiter les requêtes, en accélérant le processus de certains. Sans contrôleur aysnc, tout le monde attend que la personne en face soit complètement traitée. MAIS n'oubliez pas que dans MVC vous avez beaucoup de pilotes de bus sur un seul bus, donc l'async n'est pas un choix automatique.

K. Bob
la source
8
Très belle analogie. Je vous remercie.
Pittsburgh DBA
J'ai aimé la description. Merci
Omer Cansizoglu
Excellent moyen de l'expliquer.Merci,
Anand Vyas
Votre réponse en combinaison avec la réponse de Darin résume tout le mécanisme derrière les contrôleurs asynchrones, ce que c'est, et surtout ce qu'il n'est pas!
Nirman
C'est une belle analogie, mais ma seule question est la suivante: le gars qui fouille dans ses poches dans votre analogie serait une sorte de travail / traitement dans notre application ... alors quels processus fonctionnent une fois que nous libérons le fil? Sûrement c'est un autre fil? Alors, que gagnons-nous ici?
Tomuke
10

Il y a deux concepts en jeu ici. Tout d'abord, nous pouvons faire fonctionner notre code en parallèle pour exécuter plus rapidement ou programmer du code sur un autre thread pour éviter de faire attendre l'utilisateur. L'exemple que vous aviez

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Do an advanced looging here which takes a while
    });

    return View();
}

appartient à la deuxième catégorie. L'utilisateur obtiendra une réponse plus rapide mais la charge de travail totale sur le serveur est plus élevée car il doit faire le même travail + gérer le threading.

Un autre exemple de ceci serait:

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Make async web request to twitter with WebClient.DownloadString()
    });

    Task.Factory.StartNew(() => { 
        //Make async web request to facebook with WebClient.DownloadString()
    });


    //wait for both to be ready and merge the results

    return View();
}

Étant donné que les requêtes s'exécutent en parallèle, l'utilisateur n'aura pas à attendre aussi longtemps que si elles étaient effectuées en série. Mais vous devez comprendre que nous utilisons plus de ressources ici que si nous exécutions en série parce que nous exécutons le code sur de nombreux threads pendant que nous avons également un thread en attente.

C'est parfaitement bien dans un scénario client. Et il est assez courant d'envelopper du code synchrone de longue durée dans une nouvelle tâche (l'exécuter sur un autre thread), ainsi que de garder l'interface utilisateur réactive ou de la mettre en parallèle pour la rendre plus rapide. Un fil est toujours utilisé pendant toute la durée. Sur un serveur avec une charge élevée, cela pourrait se retourner contre vous car vous utilisez en fait plus de ressources. C'est ce dont les gens vous ont mis en garde

Les contrôleurs async dans MVC ont cependant un autre objectif. Le but ici est d'éviter d'avoir des threads assis autour de ne rien faire (qui peut nuire à l'évolutivité). Cela n'a vraiment d'importance que si les API que vous appelez ont des méthodes asynchrones. Comme WebClient.DowloadStringAsync ().

Le fait est que vous pouvez laisser votre thread être renvoyé pour gérer les nouvelles demandes jusqu'à ce que la demande Web soit terminée, où il vous appellera un rappel qui obtient le même thread ou un nouveau thread et termine la demande.

J'espère que vous comprenez la différence entre asynchrone et parallèle. Considérez le code parallèle comme du code dans lequel votre thread se trouve et attendez le résultat. Alors que le code asynchrone est un code dans lequel vous serez averti lorsque le code est terminé et que vous pourrez recommencer à travailler dessus, en attendant, le thread peut faire d'autres travaux.

Mikael Eliasson
la source
6

Les applications peuvent mieux évoluer si les opérations s'exécutent de manière asynchrone, mais uniquement si des ressources sont disponibles pour traiter les opérations supplémentaires .

Les opérations asynchrones garantissent que vous ne bloquez jamais une action car une action existante est en cours. ASP.NET a un modèle asynchrone qui permet à plusieurs demandes de s'exécuter côte à côte. Il serait possible de mettre les demandes en file d'attente et de les traiter FIFO, mais cela ne serait pas bien mis à l'échelle lorsque vous avez des centaines de demandes en file d'attente et que chaque demande prend 100 ms à traiter.

Si vous avez un volume de trafic énorme, il vaut peut- être mieux ne pas effectuer vos requêtes de manière asynchrone, car il se peut qu'il n'y ait pas de ressources supplémentaires pour traiter les requêtes . S'il n'y a pas de ressources de rechange, vos demandes sont obligées de faire la queue, prennent plus de temps ou échouent carrément, auquel cas la surcharge asynchrone (mutex et opérations de changement de contexte) ne vous donne rien.

En ce qui concerne ASP.NET, vous n'avez pas le choix - il utilise un modèle asynchrone, car c'est ce qui a du sens pour le modèle serveur-client. Si vous deviez écrire votre propre code en interne qui utilise un modèle asynchrone pour tenter de mieux évoluer, à moins que vous n'essayiez de gérer une ressource partagée entre toutes les demandes, vous ne verrez aucune amélioration car elles sont déjà encapsulées dans un processus asynchrone qui ne bloque rien d'autre.

En fin de compte, tout cela est subjectif jusqu'à ce que vous regardiez réellement ce qui cause un goulot d'étranglement dans votre système. Parfois, il est évident qu'un modèle asynchrone aidera (en empêchant un blocage des ressources en file d'attente). En fin de compte, seules la mesure et l'analyse d'un système peuvent indiquer où vous pouvez gagner en efficacité.

Éditer:

Dans votre exemple, l' Task.Factory.StartNewappel mettra en file d'attente une opération sur le pool de threads .NET. La nature des threads du pool de threads doit être réutilisée (pour éviter le coût de création / destruction de nombreux threads). Une fois l'opération terminée, le thread est relâché dans le pool pour être réutilisé par une autre demande (le garbage collector n'intervient pas réellement à moins que vous ayez créé des objets dans vos opérations, auquel cas ils sont collectés normalement. cadrage).

En ce qui concerne ASP.NET, il n'y a pas d'opération spéciale ici. La demande ASP.NET se termine sans égard à la tâche asynchrone. Le seul problème peut être si votre pool de threads est saturé (c'est-à-dire qu'il n'y a pas de threads disponibles pour traiter la demande pour le moment et les paramètres du pool ne permettent pas de créer plus de threads), auquel cas la requête est bloquée en attendant de démarrer le tâche jusqu'à ce qu'un thread de pool devienne disponible.

Paul Turner
la source
Merci! Après avoir lu votre réponse, j'ai édité la question avec un exemple de code. Pouvez-vous jeter un oeil?
tugberk
Vous avez une phrase magique pour moi: l' Task.Factory.StartNewappel mettra en file d'attente une opération sur le pool de threads .NET. . Dans ce contexte, lequel est correct ici: 1-) Il crée un nouveau thread et quand c'est fait, ce thread retourne à threadpool et attend là pour être réutilisé à nouveau. 2-) Il obtient un thread de threadpool et ce thread retourne à threadpool et attend là pour être réutilisé à nouveau. 3-) Il adopte l'approche la plus efficace et peut faire l'un ou l'autre.
tugberk
1
Le pool de threads crée des threads lorsqu'ils sont requis et recycle les threads lorsqu'ils ne sont pas utilisés. Son comportement exact a varié selon les versions de CLR. Vous pouvez trouver des informations spécifiques à ce sujet ici msdn.microsoft.com/en-us/library/0ka9477y.aspx
Paul Turner
Cela commence à prendre forme dans mon esprit maintenant. Donc, CLR possède le pool de threads, non? Par exemple, une application WPF a également une notion de pool de threads et traite également de pool de threads.
tugberk
1
Le pool de threads est sa propre chose au sein du CLR. D'autres composants qui sont "conscients" du pool indiquent qu'ils utilisent les threads du pool de threads (le cas échéant) plutôt que de créer et de détruire les leurs. La création ou la destruction d'un thread est une opération relativement coûteuse, de sorte que l'utilisation du pool représente un gain d'efficacité important sur les opérations de courte durée.
Paul Turner
2

Oui, ils utilisent un thread du pool de threads. Il existe en fait un excellent guide de MSDN qui répondra à toutes vos questions et plus encore. Je l'ai trouvé très utile dans le passé. Vérifiez-le!

http://msdn.microsoft.com/en-us/library/ee728598.aspx

Pendant ce temps, les commentaires + suggestions que vous entendez sur le code asynchrone doivent être pris avec un grain de sel. Pour commencer, le simple fait de rendre quelque chose asynchrone ne permet pas nécessairement de mieux évoluer et, dans certains cas, peut aggraver la mise à l'échelle de votre application. L'autre commentaire que vous avez publié sur "un énorme volume de trafic ..." n'est également correct que dans certains contextes. Cela dépend vraiment de ce que font vos opérations et de la manière dont elles interagissent avec les autres parties du système.

En bref, beaucoup de gens ont beaucoup d'opinions sur l'async, mais elles peuvent ne pas être correctes hors de leur contexte. Je dirais que vous vous concentrez sur vos problèmes exacts et que vous effectuez des tests de performances de base pour voir quels contrôleurs asynchrones, etc. gèrent réellement avec votre application.

AR
la source
J'ai lu ce document peut-être des centaines de fois et j'ai encore tellement de confusion (peut-être que le problème est moi, qui sait). Lorsque vous regardez autour de vous, vous voyez tellement de commentaires incohérents sur l'asynchronie sur ASP.NET MVC comme vous pouvez le voir sur ma question.
tugberk
pour la dernière phrase: dans une action de contrôleur, j'interrogeais une base de données 5 fois séparément (je devais le faire) et tout cela prenait environ 400 ms. Ensuite, j'ai implémenté AsyncController et je les ai exécutés en parallèle. Le temps de réponse a été considérablement réduit à env. 200 ms. Mais je n'ai aucune idée du nombre de threads qu'il crée, de ce qui arrive à ces threads une fois que j'en ai fini avec eux, GCvient les nettoyer juste après que j'ai terminé afin que mon application n'ait pas de fuite de mémoire, ainsi de suite. Toute idée sur cette partie.
tugberk
attachez un débogueur et découvrez.
AR
0

Tout d'abord, ce n'est pas MVC mais l'IIS qui gère le pool de threads. Ainsi, toute demande qui vient à l'application MVC ou ASP.NET est servie à partir de threads qui sont maintenus dans le pool de threads. Seulement en rendant l'application Asynch, il invoque cette action dans un thread différent et libère le thread immédiatement afin que d'autres requêtes puissent être prises.

J'ai expliqué la même chose avec une vidéo détaillée ( http://www.youtube.com/watch?v=wvg13n5V0V0/ "MVC Asynch controllers and thread starvation") qui montre comment la famine de thread se produit dans MVC et comment elle est minimisée en utilisant MVC Contrôleurs asynchrones J'ai également mesuré les files d'attente de demandes à l'aide de perfmon afin que vous puissiez voir comment les files d'attente de demandes sont réduites pour l'asynchrone MVC et son pire pour les opérations de synchronisation.

Shivprasad Koirala
la source