Utilisation de ThreadPool.QueueUserWorkItem dans ASP.NET dans un scénario à trafic élevé

111

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).

Michael Hart
la source
L'intrigue s'épaissit - j'ai trouvé cet article ( blogs.msdn.com/nicd/archive/2007/04/16/… ), que je n'arrive pas à décoder. D'une part, il semble dire qu'IIS 6.0+ traite toujours les demandes sur les threads de travail du pool de threads (et que les versions antérieures pourraient le faire), mais il y a ceci: "Cependant, si vous utilisez de nouvelles pages asynchrones .NET 2.0 ( Async = "true") ou ThreadPool.QueueUserWorkItem (), alors la partie asynchrone du traitement sera effectuée dans [un thread de port d'achèvement]. " La partie asynchrone du traitement ?
Jeff Sternal
Une autre chose - cela devrait être assez facile à tester sur une installation IIS 6.0+ (que je n'ai pas pour le moment) en vérifiant si les threads de travail disponibles du pool de threads sont inférieurs à ses threads de travail maximum, puis en faisant la même chose dans la file d'attente objets de travail.
Jeff Sternal

Réponses:

104

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 le ThreadPoolqui répondent aux QueueUserWorkItemdemandes.

Oui, c'est vrai, vous pouvez affamer le ThreadPooldans 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é pour QueueUserWorkItemest é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 Beginet End. Comme dans Stream.BeginRead, Socket.BeginConnect, WebRequest.BeginGetResponseet 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 une Threadinstance 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 utilisiez ThreadPoolou 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.

Aaronaught
la source
Merci pour cette réponse détaillée et vous avez raison, j'essaie d'utiliser des méthodes asynchrones lorsque cela est possible (couplées avec des contrôleurs asynchrones dans ASP.NET MVC). Dans le cas de mon exemple, avec l'enregistreur distant, c'est exactement ce que je peux faire. C'est un problème de conception intéressant car il pousse la gestion asynchrone jusqu'au niveau le plus bas de votre code (c'est-à-dire l'implémentation de l'enregistreur), au lieu de pouvoir le décider, par exemple, au niveau du contrôleur (dans ce dernier cas , vous auriez besoin, par exemple, de deux implémentations de logger pour pouvoir choisir).
Michael Hart
@Michael: Les callbacks Async sont généralement assez faciles à encapsuler si vous voulez augmenter les niveaux; vous pouvez créer une façade autour des méthodes asynchrones et les envelopper avec une seule méthode qui utilise un 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.
Aaronaught le
Bien que, comme point d'intérêt, seul le .NET ThreadPoolvous 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.
Aaronaught
2
Ports d'achèvement d'E / S (IOCP). la description de l'IOCP n'est pas tout à fait correcte. dans IOCP, vous avez un nombre statique de threads de travail qui travaillent à tour de rôle sur TOUTES les tâches en attente. à ne pas confondre avec les pools de threads qui peuvent être de taille fixe ou dynamique MAIS avoir un thread par tâche - évolue terriblement. contrairement à ASYNC, vous n'avez pas un thread par tâche. un thread IOCP peut travailler un peu sur la tâche 1, puis passer à la tâche 3, la tâche 2, puis revenir à la tâche 1. les états de session de tâche sont enregistrés et sont transmis entre les threads.
MickyD
1
Qu'en est-il des insertions de base de données? Existe-t-il une commande SQL ASYNC (comme Execute)? Les insertions de base de données sont à peu près l'opération d'E / S la plus lente (à cause du verrouillage) et le fait que le thread principal attende que la ou les lignes soient insérées n'est qu'un gaspillage de cycles CPU.
Ian Thompson
45

Selon Thomas Marquadt de l'équipe ASP.NET chez Microsoft, il est sûr d'utiliser le ThreadPool ASP.NET (QueueUserWorkItem).

De l'article :

Q) Si mon application ASP.NET utilise des threads CLR ThreadPool, ne vais-je pas mourir de faim ASP.NET, qui utilise également le CLR ThreadPool pour exécuter les demandes? ..

A) Pour résumer, ne vous inquiétez pas de priver ASP.NET de threads, et si vous pensez qu'il y a un problème ici, faites-le moi savoir et nous nous en occuperons.

Q) Dois-je créer mes propres threads (nouveau thread)? Ce ne sera pas mieux pour ASP.NET, car il utilise le CLR ThreadPool.

A) Merci de ne pas le faire. Ou pour le dire autrement, non !!! Si vous êtes vraiment intelligent - beaucoup plus intelligent que moi - alors vous pouvez créer vos propres fils; sinon, n'y pensez même pas. Voici quelques raisons pour lesquelles vous ne devriez pas créer fréquemment de nouveaux threads:

  1. C'est très cher, comparé à QueueUserWorkItem ... Au fait, si vous pouvez écrire un meilleur ThreadPool que le CLR, je vous encourage à postuler pour un emploi chez Microsoft, car nous recherchons définitivement des personnes comme vous !.
Tim P.
la source
4

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).

Soie de midi
la source
4
Je ne suis pas d'accord pour dire que cette déclaration générale est toujours vraie - en particulier pour les tâches non critiques. Créer un service Windows, uniquement à des fins de journalisation asynchrone, semble définitivement exagéré. De plus, cette option n'est pas toujours disponible (pouvoir déployer MSMQ et / ou un service Windows).
Michael Hart
Bien sûr, mais c'est la manière «standard» d'implémenter des tâches asynchrones à partir d'un site Web (thème de file d'attente par rapport à un autre processus).
Noon Silk
2
Toutes les tâches asynchrones ne sont pas créées de la même manière, c'est pourquoi, par exemple, des pages asynchrones existent dans ASP.NET. Si je veux récupérer un résultat à partir d'un service Web distant pour l'afficher, je ne vais pas le faire via MSMQ. Dans ce cas, j'écris dans un journal en utilisant un poste distant. Cela ne convient pas au problème d'écrire un service Windows, ni de connecter MSMQ pour cela (et je ne peux pas non plus, car cette application particulière est sur Azure).
Michael Hart
1
Considérez: vous écrivez sur un hôte distant? Et si cet hôte est en panne ou inaccessible? Voulez-vous réessayer votre écriture? Peut-être que vous le ferez, peut-être que vous ne le ferez pas. Avec votre implémentation, il est difficile de réessayer. Avec le service, cela devient assez trivial. J'apprécie que vous ne puissiez pas le faire, et je laisserai quelqu'un d'autre répondre aux problèmes spécifiques liés à la création de fils à partir de sites Web [par exemple, si votre fil n'était pas en arrière-plan, etc.], mais je décris le «bon» façon de le faire. Je ne suis pas familier avec azure, même si j'ai utilisé ec2 (vous pouvez installer un système d'exploitation là-dessus, donc tout va bien).
Noon Silk
@silky, merci pour les commentaires. J'avais dit «non critique» pour éviter cette solution plus lourde (mais durable). J'ai clarifié la question afin qu'il soit clair que je ne demande pas les meilleures pratiques concernant les éléments de travail en file d'attente. Azure prend en charge ce type de scénario (il a son propre stockage de file d'attente) - mais l'opération de mise en file d'attente est trop coûteuse pour la journalisation synchrone, donc j'aurais quand même besoin d'une solution asynchrone. Dans mon cas, je suis conscient des pièges de l'échec, mais je ne vais pas ajouter plus d'infrastructure au cas où ce fournisseur de journalisation particulier échouerait - j'ai aussi d'autres fournisseurs de journalisation.
Michael Hart
4

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.

Sam
la source
2

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

Thomas
la source
Ce lien est très utile, merci pour cela Thomas. Je serais également intéressé de savoir ce que vous pensez de la réponse de @ Aaronaught.
Michael Hart
Je suis d'accord avec Aaronaught et j'ai dit la même chose dans mon article de blog. Je l'ai mis de cette façon: «Pour tenter de simplifier cette décision, vous ne devriez passer [à un autre thread] que si vous bloquez autrement le thread de requête ASP.NET pendant que vous ne faites rien. C'est une simplification excessive, mais j'essaye pour rendre la décision simple. " En d'autres termes, ne le faites pas pour un travail de calcul non bloquant, mais faites-le si vous faites des demandes de service Web asynchrone à un serveur distant. Écoutez Aaronaught! :)
Thomas
1

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.

Samuel Neff
la source
1
@Sam, j'utilise en fait log4net et je ne vois pas les journaux en cours d'écriture dans un thread séparé - y a-t-il une sorte d'option que je dois activer?
Michael Hart
1

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 :

Il existe un pool de threads par processus. Le pool de threads a une taille par défaut de 250 threads de travail par processeur disponible et 1000 threads d'achèvement d'E / S. Le nombre de threads dans le pool de threads peut être modifié à l'aide de la méthode SetMaxThreads. Chaque thread utilise la taille de pile par défaut et s'exécute avec la priorité par défaut.

Christopher Nobles
la source
Une application qui s'exécute dans un seul processus est entièrement capable de s'arrêter! (Ou du moins dégrader suffisamment ses propres performances pour faire du pool de threads une proposition perdante.)
Jeff Sternal
Je suppose donc que les requêtes ASP.NET utilisent les threads d'achèvement d'E / S (par opposition aux threads de travail) - est-ce correct?
Michael Hart
De l'article de Fritz Onion, j'ai lié ma réponse: «Ce paradigme change [d'IIS 5.0 à IIS 6.0] la façon dont les demandes sont gérées dans ASP.NET. Au lieu de distribuer les demandes de inetinfo.exe au processus de travail ASP.NET, http. sys met directement chaque demande en file d'attente dans le processus approprié. Ainsi, toutes les demandes sont désormais traitées par des threads de travail tirés du pool de threads CLR et jamais sur des threads d'E / S. " (je souligne)
Jeff Sternal
Hmmm, je ne suis toujours pas tout à fait sûr ... Cet article date de juin 2003. Si vous lisez celui-ci de mai 2004 (certes encore assez ancien), il dit: "La page de test Sleep.aspx peut être utilisée pour conserver un ASP .NET I / O thread busy ", où Sleep.aspx met simplement le thread en cours d'exécution en veille: msdn.microsoft.com/en-us/library/ms979194.aspx - Quand j'aurai une chance, je verrai si je peux coder cet exemple et tester sur IIS 7 et .NET 3.5
Michael Hart
Oui, le texte de ce paragraphe est déroutant. Plus loin dans cette section, il renvoie à une rubrique de support ( support.microsoft.com/default.aspx?scid=kb;EN-US;816829 ) qui clarifie les choses: l'exécution de requêtes sur les threads d'achèvement d'E / S était un .NET Framework 1.0 problème qui a été résolu dans le package de correctifs cumulatifs de juin 2003 ASP.NET 1.1 (après quoi «TOUTES les demandes s'exécutent désormais sur les threads de travail»). Plus important encore, cet exemple montre clairement que le pool de threads ASP.NET est le même pool de threads exposé par System.Threading.ThreadPool.
Jeff Sternal
1

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.

  1. Une demande est faite pour une page
  2. Le HTML est renvoyé
  3. Le Html indique au client de faire d'autres requets (js, css, images, etc.)
  4. De plus amples informations sont servies

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.

Threads vs processus

Les threads et les processus sont des méthodes de parallélisation d'une application. Cependant, les processus sont des unités d'exécution indépendantes qui contiennent leurs propres informations d'état, utilisent leurs propres espaces d'adressage et interagissent uniquement entre eux via des mécanismes de communication interprocessus (généralement gérés par le système d'exploitation). Les applications sont généralement divisées en processus au cours de la phase de conception, et un processus maître génère explicitement des sous-processus lorsqu'il est logique de séparer logiquement des fonctionnalités d'application importantes. Les processus, en d'autres termes, sont une construction architecturale.

En revanche, un thread est une construction de codage qui n'affecte pas l'architecture d'une application. Un seul processus peut contenir plusieurs threads; tous les threads d'un processus partagent le même état et le même espace mémoire et peuvent communiquer directement entre eux, car ils partagent les mêmes variables.

La source

Ty.
la source
3
@Ty, merci pour votre contribution, mais je suis bien conscient du fonctionnement d'un serveur Web et ce n'est pas vraiment pertinent pour la question - encore une fois, comme je l'ai dit dans la question, je ne demande pas de conseils à ce sujet en tant qu'architecture problème. Je demande des informations techniques spécifiques. Quant au fait que "la préoccupation de l'enregistreur" devrait déjà avoir un processus asynchrone en place - comment pensez-vous que le processus asynchrone devrait être écrit par l'implémentation de l'enregistreur?
Michael Hart
0

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.

Timbo
la source
0

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.

bmm6o
la source
0

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.

var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;

ParallelOptions po = new ParallelOptions();
            po.CancellationToken = ts.Token;
            po.MaxDegreeOfParallelism = 6; //limit here

 Task.Factory.StartNew(()=>
                {                        
                  Parallel.ForEach(collectionList, po, (collectionItem) =>
                  {
                     //Code Here PostLog(logEvent);
                  }
                });
khayam
la source