J'ai toujours eu l'impression que l'utilisation de ThreadPool pour des tâches d'arrière-plan de courte durée (disons non critiques) était considérée comme la meilleure pratique, même dans ASP.NET, mais je suis ensuite tombé sur cet article qui semble suggérer le contraire - le L'argument étant que vous devez quitter le ThreadPool pour traiter les requêtes liées à ASP.NET.
Alors, voici comment j'ai effectué jusqu'à présent de petites tâches asynchrones:
ThreadPool.QueueUserWorkItem(s => PostLog(logEvent))
Et l'article suggère à la place de créer un fil de discussion explicitement, similaire à:
new Thread(() => PostLog(logEvent)){ IsBackground = true }.Start()
La première méthode a l'avantage d'être gérée et limitée, mais il est possible (si l'article est correct) que les tâches d'arrière-plan se disputent alors des threads avec des gestionnaires de requêtes ASP.NET. La deuxième méthode libère le ThreadPool, mais au prix d'être illimité et donc potentiellement d'utiliser trop de ressources.
Ma question est donc la suivante: les conseils contenus dans l'article sont-ils corrects?
Si votre site recevait tellement de trafic que votre ThreadPool était plein, alors vaut-il mieux sortir de la bande, ou un ThreadPool complet impliquerait-il que vous atteignez la limite de vos ressources de toute façon, auquel cas vous ne devrait pas essayer de démarrer vos propres fils?
Clarification: je demande simplement dans le cadre de petites tâches asynchrones non critiques (par exemple, la journalisation à distance), des éléments de travail non coûteux qui nécessiteraient un processus séparé (dans ces cas, je conviens que vous aurez besoin d'une solution plus robuste).
la source
Réponses:
D'autres réponses semblent laisser de côté le point le plus important:
À moins que vous n'essayiez de paralléliser une opération gourmande en ressources processeur afin de la faire plus rapidement sur un site à faible charge, il est inutile d'utiliser un thread de travail.
Cela vaut pour les threads gratuits, créés par
new Thread(...)
, et les threads de travail dans leThreadPool
qui répondent auxQueueUserWorkItem
demandes.Oui, c'est vrai, vous pouvez affamer le
ThreadPool
dans un processus ASP.NET en mettant trop d'éléments de travail en file d'attente. Cela empêchera ASP.NET de traiter d'autres demandes. Les informations contenues dans l'article sont exactes à cet égard; le même pool de threads utilisé pourQueueUserWorkItem
est également utilisé pour traiter les demandes.Mais si vous mettez en file d'attente suffisamment d'éléments de travail pour provoquer cette famine, vous devriez alors affamer le pool de threads! Si vous exécutez littéralement des centaines d'opérations gourmandes en ressources processeur en même temps, à quoi cela sert-il d'avoir un autre thread de travail pour traiter une requête ASP.NET, alors que la machine est déjà surchargée? Si vous rencontrez cette situation, vous devez complètement repenser!
La plupart du temps, je vois ou j'entends parler de code multi-thread utilisé de manière inappropriée dans ASP.NET, ce n'est pas pour mettre en file d'attente un travail intensif en CPU. C'est pour mettre en file d'attente le travail lié aux E / S. Et si vous souhaitez effectuer des travaux d'E / S, vous devez utiliser un thread d'E / S (port d'achèvement d'E / S).
Plus précisément, vous devez utiliser les rappels asynchrones pris en charge par la classe de bibliothèque que vous utilisez. Ces méthodes sont toujours très clairement étiquetées; ils commencent par les mots
Begin
etEnd
. Comme dansStream.BeginRead
,Socket.BeginConnect
,WebRequest.BeginGetResponse
et ainsi de suite.Ces méthodes font utiliser
ThreadPool
, mais ils utilisent IOCPs, qui ne pas interférer avec les demandes ASP.NET. Il s'agit d'un type spécial de thread léger qui peut être "réveillé" par un signal d'interruption du système d'E / S. Et dans une application ASP.NET, vous avez normalement un thread d'E / S pour chaque thread de travail, de sorte que chaque demande peut avoir une opération asynchrone en file d'attente. C'est littéralement des centaines d'opérations asynchrones sans aucune dégradation significative des performances (en supposant que le sous-système d'E / S puisse suivre). C'est bien plus que vous n'en aurez jamais besoin.Gardez simplement à l'esprit que les délégués asynchrones ne fonctionnent pas de cette façon - ils finiront par utiliser un thread de travail, tout comme
ThreadPool.QueueUserWorkItem
. Ce ne sont que les méthodes asynchrones intégrées des classes de la bibliothèque .NET Framework qui sont capables de le faire. Vous pouvez le faire vous-même, mais c'est compliqué et un peu dangereux et probablement au-delà de la portée de cette discussion.La meilleure réponse à cette question, à mon avis, est de ne pas utiliser le
ThreadPool
ou uneThread
instance d' arrière-plan dans ASP.NET . Ce n'est pas du tout comme faire tourner un fil dans une application Windows Forms, où vous le faites pour garder l'interface utilisateur réactive et ne vous souciez pas de son efficacité. Dans ASP.NET, votre préoccupation est le débit , et tout ce changement de contexte sur tous ces threads de travail va absolument tuer votre débit, que vous utilisiezThreadPool
ou non.S'il vous plaît, si vous vous trouvez en train d'écrire du code de thread dans ASP.NET - demandez-vous s'il pourrait ou non être réécrit pour utiliser des méthodes asynchrones préexistantes, et si ce n'est pas le cas, veuillez déterminer si vous avez vraiment, vraiment besoin du code. pour exécuter dans un fil d'arrière-plan du tout. Dans la majorité des cas, vous ajouterez probablement de la complexité sans aucun avantage net.
la source
Action<T>
comme rappel, par exemple. Si vous voulez dire que le choix d'utiliser un thread de travail ou un thread d'E / S se produit au niveau le plus bas, c'est intentionnel; seul ce niveau peut décider s'il a besoin ou non d'un IOCP.ThreadPool
vous limite de cette façon, probablement parce qu'ils ne faisaient pas confiance aux développeurs pour bien faire les choses. Le pool de threads Windows non géré a une API très similaire, mais vous permet en fait de choisir le type de thread.Selon Thomas Marquadt de l'équipe ASP.NET chez Microsoft, il est sûr d'utiliser le ThreadPool ASP.NET (QueueUserWorkItem).
De l'article :
la source
Les sites Web ne devraient pas faire le tour des threads.
Vous déplacez généralement cette fonctionnalité vers un service Windows avec lequel vous communiquez ensuite (j'utilise MSMQ pour leur parler).
-- Éditer
J'ai décrit une implémentation ici: Traitement en arrière-plan basé sur la file d'attente dans l'application Web ASP.NET MVC
-- Éditer
Pour expliquer pourquoi c'est encore mieux que de simples threads:
En utilisant MSMQ, vous pouvez communiquer avec un autre serveur. Vous pouvez écrire dans une file d'attente sur plusieurs machines, donc si vous déterminez, pour une raison quelconque, que votre tâche d'arrière-plan utilise trop les ressources du serveur principal, vous pouvez simplement la déplacer de manière assez triviale.
Il vous permet également de traiter par lots toute tâche que vous essayiez de faire (envoyer des e-mails / quoi que ce soit).
la source
Je pense vraiment que la pratique générale pour un travail asynchrone rapide et de faible priorité dans ASP.NET serait d'utiliser le pool de threads .NET, en particulier pour les scénarios à fort trafic, car vous souhaitez que vos ressources soient limitées.
De plus, l'implémentation du threading est masquée - si vous commencez à générer vos propres threads, vous devez également les gérer correctement. Je ne dis pas que vous ne pouvez pas le faire, mais pourquoi réinventer cette roue?
Si les performances deviennent un problème et que vous pouvez établir que le pool de threads est le facteur limitant (et non les connexions de base de données, les connexions réseau sortantes, la mémoire, les délais d'expiration des pages, etc.), vous modifiez la configuration du pool de threads pour autoriser plus de threads de travail, plus de demandes en file d'attente. , etc.
Si vous n'avez pas de problème de performances, choisir de générer de nouveaux threads pour réduire les conflits avec la file d'attente de requêtes ASP.NET est une optimisation prématurée classique.
Idéalement, vous n'auriez pas besoin d'utiliser un thread séparé pour effectuer une opération de journalisation - activez simplement le thread d'origine pour terminer l'opération le plus rapidement possible, c'est là que MSMQ et un thread / processus consommateur séparé entrent en scène. Je conviens que c'est plus lourd et plus de travail à mettre en œuvre, mais vous avez vraiment besoin de la durabilité ici - la volatilité d'une file d'attente partagée en mémoire épuisera rapidement son accueil.
la source
Vous devez utiliser QueueUserWorkItem et éviter de créer de nouveaux threads comme vous éviteriez la peste. Pour un visuel qui explique pourquoi vous ne mourrez pas de faim ASP.NET, puisqu'il utilise le même ThreadPool, imaginez un jongleur très habile utilisant deux mains pour garder une demi-douzaine de quilles, épées ou autre en vol. Pour avoir une idée des raisons pour lesquelles la création de vos propres threads est mauvaise, imaginez ce qui se passe à Seattle aux heures de pointe lorsque les rampes d'entrée très utilisées de l'autoroute permettent aux véhicules d'entrer immédiatement dans la circulation au lieu d'utiliser une lumière et de limiter le nombre d'entrées à une toutes les quelques secondes. . Enfin, pour une explication détaillée, veuillez consulter ce lien:
http://blogs.msdn.com/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx
Merci, Thomas
la source
Cet article n'est pas correct. ASP.NET possède son propre pool de threads, threads de travail gérés, pour traiter les demandes ASP.NET. Ce pool est généralement composé de quelques centaines de threads et est séparé du pool ThreadPool, qui est un plus petit multiple de processeurs.
L'utilisation de ThreadPool dans ASP.NET n'interférera pas avec les threads de travail ASP.NET. L'utilisation de ThreadPool est très bien.
Il serait également acceptable de configurer un seul thread qui est juste pour la journalisation des messages et d'utiliser le modèle producteur / consommateur pour passer les messages de journaux à ce thread. Dans ce cas, puisque le thread est de longue durée, vous devez créer un seul nouveau thread pour exécuter la journalisation.
Utiliser un nouveau fil pour chaque message est définitivement exagéré.
Une autre alternative, si vous ne parlez que de journalisation, consiste à utiliser une bibliothèque comme log4net. Il gère la journalisation dans un thread séparé et prend en charge tous les problèmes de contexte qui pourraient survenir dans ce scénario.
la source
Je dirais que l'article est faux. Si vous exécutez une grande boutique .NET, vous pouvez utiliser en toute sécurité le pool sur plusieurs applications et plusieurs sites Web (en utilisant des pools d'applications séparés), simplement en vous basant sur une déclaration dans la documentation ThreadPool :
la source
System.Threading.ThreadPool
.On m'a posé une question similaire au travail la semaine dernière et je vais vous donner la même réponse. Pourquoi utilisez-vous des applications Web multi-thread par demande? Un serveur Web est un système fantastique fortement optimisé pour fournir de nombreuses requêtes en temps opportun (ie multi threading). Pensez à ce qui se passe lorsque vous demandez presque n'importe quelle page sur le Web.
Vous donnez l'exemple de la journalisation à distance, mais cela devrait être une préoccupation de votre enregistreur. Un processus asynchrone doit être en place pour recevoir les messages en temps opportun. Sam souligne même que votre enregistreur (log4net) devrait déjà prendre en charge cela.
Sam a également raison en ce que l'utilisation du pool de threads sur le CLR ne causera pas de problèmes avec le pool de threads dans IIS. La chose à considérer ici cependant, c'est que vous ne créez pas de threads à partir d'un processus, vous créez de nouveaux threads à partir des threads du pool de threads IIS. Il y a une différence et la distinction est importante.
La source
la source
Je ne suis pas d'accord avec l'article référencé (C # feeds.com). Il est facile de créer un nouveau fil mais dangereux. Le nombre optimal de threads actifs à exécuter sur un seul cœur est en fait étonnamment faible - moins de 10. Il est beaucoup trop facile de faire perdre du temps à la machine à changer de thread si des threads sont créés pour des tâches mineures. Les threads sont une ressource qui nécessite une gestion. L'abstraction WorkItem est là pour gérer cela.
Il y a ici un compromis entre réduire le nombre de threads disponibles pour les requêtes et créer trop de threads pour permettre à l'un d'entre eux de traiter efficacement. C'est une situation très dynamique mais je pense qu'elle devrait être gérée activement (dans ce cas par le pool de threads) plutôt que de laisser au processeur le soin de garder une longueur d'avance sur la création de threads.
Enfin, l'article fait des déclarations assez radicales sur les dangers de l'utilisation du ThreadPool, mais il a vraiment besoin de quelque chose de concret pour les soutenir.
la source
Que IIS utilise ou non le même ThreadPool pour gérer les demandes entrantes semble difficile d'obtenir une réponse définitive et semble également avoir changé au fil des versions. Il semblerait donc judicieux de ne pas utiliser excessivement les threads ThreadPool, afin que IIS en ait beaucoup disponibles. D'un autre côté, créer votre propre fil pour chaque petite tâche semble être une mauvaise idée. Vraisemblablement, vous avez une sorte de verrouillage dans votre journalisation, donc un seul thread pourrait progresser à la fois, et le reste se relayerait simplement pour être planifié et non planifié (sans parler de la surcharge liée à la création d'un nouveau thread). Essentiellement, vous rencontrez les problèmes exacts que ThreadPool a été conçu pour éviter.
Il semble qu'un compromis raisonnable serait pour votre application d'allouer un seul thread de journalisation auquel vous pourriez transmettre des messages. Vous devriez faire attention à ce que l'envoi de messages soit aussi rapide que possible afin de ne pas ralentir votre application.
la source
Vous pouvez utiliser Parallel.For ou Parallel.ForEach et définir la limite des threads possibles que vous souhaitez allouer pour fonctionner correctement et éviter la famine du pool.
Cependant, en cours d'exécution en arrière-plan, vous devrez utiliser le style TPL pur ci-dessous dans l'application Web ASP.Net.
la source