Verwenden von ThreadPool.QueueUserWorkItem in ASP.NET in einem Szenario mit hohem Datenverkehr

111

Ich hatte immer den Eindruck, dass die Verwendung des ThreadPool für (sagen wir unkritische) kurzlebige Hintergrundaufgaben selbst in ASP.NET als bewährte Methode angesehen wurde, aber dann bin ich auf diesen Artikel gestoßen, der etwas anderes zu suggerieren scheint - das Argument ist, dass Sie den ThreadPool verlassen sollten, um ASP.NET-bezogene Anforderungen zu bearbeiten.

So habe ich bisher kleine asynchrone Aufgaben ausgeführt:

ThreadPool.QueueUserWorkItem(s => PostLog(logEvent))

Und der Artikel schlägt stattdessen vor, explizit einen Thread zu erstellen, ähnlich wie:

new Thread(() => PostLog(logEvent)){ IsBackground = true }.Start()

Die erste Methode hat den Vorteil, verwaltet und begrenzt zu werden, aber es besteht die Möglichkeit (wenn der Artikel korrekt ist), dass die Hintergrundaufgaben dann mit ASP.NET-Anforderungshandlern um Threads wetteifern. Die zweite Methode macht den ThreadPool frei, allerdings auf Kosten der Unbegrenztheit und damit möglicherweise des Verbrauchs zu vieler Ressourcen.

Meine Frage ist also, ist der Rat im Artikel richtig?

Wenn Ihre Site so viel Verkehr hatte, dass Ihr ThreadPool voll wurde, ist es besser, das Band zu verlassen, oder würde ein vollständiger ThreadPool bedeuten, dass Sie ohnehin an die Grenzen Ihrer Ressourcen stoßen, in diesem Fall Sie sollten Sie nicht versuchen, Ihre eigenen Threads zu starten?

Klarstellung: Ich frage nur im Rahmen kleiner unkritischer asynchroner Aufgaben (z. B. Remote-Protokollierung) nach nicht teuren Arbeitselementen, die einen separaten Prozess erfordern würden (in diesen Fällen stimme ich zu, dass Sie eine robustere Lösung benötigen).

Michael Hart
quelle
Die Handlung wird dicker - ich habe diesen Artikel gefunden ( blogs.msdn.com/nicd/archive/2007/04/16/… ), den ich nicht ganz entschlüsseln kann. Einerseits scheint es zu heißen, dass IIS 6.0+ immer Anforderungen in Thread-Pool-Worker-Threads verarbeitet (und dass frühere Versionen dies möglicherweise tun), aber dann gibt es Folgendes : "Wenn Sie jedoch neue asynchrone .NET 2.0-Seiten verwenden ( Async = "true") oder ThreadPool.QueueUserWorkItem (), dann wird der asynchrone Teil der Verarbeitung in [einem Abschlussport-Thread] ausgeführt. " Der asynchrone Teil der Verarbeitung ?
Jeff Sternal
Eine andere Sache - dies sollte einfach genug sein, um auf einer IIS 6.0+ -Installation (die ich derzeit nicht habe) zu testen, indem überprüft wird, ob die verfügbaren Worker-Threads des Thread-Pools niedriger sind als die maximalen Worker-Threads, und dasselbe in der Warteschlange getan wird Arbeitsgegenstände.
Jeff Sternal

Antworten:

104

Andere Antworten hier scheinen den wichtigsten Punkt auszulassen:

Wenn Sie nicht versuchen, einen CPU-intensiven Vorgang zu parallelisieren, um ihn auf einer Site mit geringer Last schneller auszuführen, macht es keinen Sinn, überhaupt einen Worker-Thread zu verwenden.

Dies gilt sowohl für freie Threads, die von erstellt wurden new Thread(...), als auch für Arbeitsthreads ThreadPool, die auf QueueUserWorkItemAnforderungen reagieren .

Ja, es stimmt, Sie können das ThreadPoolin einem ASP.NET-Prozess verhungern lassen , indem Sie zu viele Arbeitselemente in die Warteschlange stellen. Dadurch wird verhindert, dass ASP.NET weitere Anforderungen verarbeitet. Die Informationen im Artikel sind in dieser Hinsicht korrekt. Der gleiche Thread-Pool, für den verwendet QueueUserWorkItemwird, wird auch zum Bearbeiten von Anforderungen verwendet.

Aber wenn Sie tatsächlich genug Arbeitselemente in die Warteschlange stellen, um diesen Hunger zu verursachen, sollten Sie den Thread-Pool hungern lassen! Wenn Sie buchstäblich Hunderte von CPU-intensiven Vorgängen gleichzeitig ausführen, was würde es nützen, wenn ein anderer Arbeitsthread eine ASP.NET-Anforderung bedient, wenn der Computer bereits überlastet ist? Wenn Sie in diese Situation geraten, müssen Sie komplett neu gestalten!

Die meiste Zeit sehe oder höre ich, dass Multithread-Code in ASP.NET unangemessen verwendet wird. Er dient nicht dazu, CPU-intensive Arbeit in die Warteschlange zu stellen. Es dient zum Einreihen von E / A-gebundener Arbeit. Und wenn Sie E / A-Arbeiten ausführen möchten, sollten Sie einen E / A-Thread (E / A-Abschlussport) verwenden.

Insbesondere sollten Sie die asynchronen Rückrufe verwenden, die von der von Ihnen verwendeten Bibliotheksklasse unterstützt werden. Diese Methoden sind immer sehr deutlich gekennzeichnet; sie beginnen mit den Worten Beginund End. Wie in Stream.BeginRead, Socket.BeginConnect, WebRequest.BeginGetResponse, und so weiter.

Diese Methoden machen verwenden die ThreadPool, aber sie benutzen IOCPs, die sie nicht mit ASP.NET - Anfragen stören. Sie sind eine spezielle Art von leichtgewichtigem Thread, der durch ein Interrupt-Signal vom E / A-System "aufgeweckt" werden kann. In einer ASP.NET-Anwendung haben Sie normalerweise einen E / A-Thread für jeden Arbeitsthread, sodass für jede einzelne Anforderung eine asynchrone Operation in die Warteschlange gestellt werden kann. Das sind buchstäblich Hunderte von asynchronen Vorgängen ohne signifikante Leistungseinbußen (vorausgesetzt, das E / A-Subsystem kann mithalten). Es ist weit mehr als Sie jemals brauchen werden.

Denken Sie daran, dass asynchrone Delegaten nicht auf diese Weise funktionieren - sie verwenden am Ende genau wie einen Arbeitsthread ThreadPool.QueueUserWorkItem. Dies können nur die integrierten asynchronen Methoden der .NET Framework-Bibliotheksklassen. Sie können es selbst tun, aber es ist kompliziert und ein bisschen gefährlich und geht wahrscheinlich über den Rahmen dieser Diskussion hinaus.

Die beste Antwort auf diese Frage ist meiner Meinung nach, dass Sie die ThreadPool oder eine Hintergrundinstanz Threadin ASP.NET nicht verwenden . Es ist überhaupt nicht so, als würde man einen Thread in einer Windows Forms-Anwendung hochfahren, in der Sie die Benutzeroberfläche reaktionsschnell halten und sich nicht darum kümmern, wie effizient sie ist. In ASP.NET ist Ihre Sorge Durchsatz , und alles , was Kontext auf allen diesen Arbeitsthreads Schalt absolut gehen zu töten Ihren Durchsatz , ob Sie die verwenden ThreadPooloder nicht.

Wenn Sie Threading-Code in ASP.NET schreiben, überlegen Sie, ob er mit bereits vorhandenen asynchronen Methoden neu geschrieben werden kann. Wenn dies nicht möglich ist, prüfen Sie bitte, ob Sie den Code wirklich wirklich benötigen überhaupt in einem Hintergrund-Thread laufen. In den meisten Fällen werden Sie wahrscheinlich die Komplexität ohne Nettonutzen erhöhen.

Aaronaught
quelle
Vielen Dank für diese ausführliche Antwort und Sie haben Recht. Ich versuche, wenn möglich asynchrone Methoden zu verwenden (gekoppelt mit asynchronen Controllern in ASP.NET MVC). In meinem Beispiel mit dem Remote Logger kann ich genau das tun. Dies ist jedoch ein interessantes Entwurfsproblem, da dadurch die asynchrone Verarbeitung bis auf die unterste Ebene Ihres Codes (dh die Logger-Implementierung) verschoben wird, anstatt beispielsweise über die Controller-Ebene (im letzteren Fall) entscheiden zu können Sie benötigen beispielsweise zwei Logger-Implementierungen, um auswählen zu können.
Michael Hart
@Michael: Asynchrone Rückrufe sind im Allgemeinen ziemlich einfach zu verpacken, wenn Sie sie um mehr Ebenen erhöhen möchten. Sie können eine Fassade um die asynchronen Methoden erstellen und sie mit einer einzelnen Methode Action<T>umschließen, die beispielsweise eine als Rückruf verwendet. Wenn Sie meinen, dass die Wahl, ob ein Arbeitsthread oder ein E / A-Thread verwendet werden soll, auf der untersten Ebene erfolgt, ist dies beabsichtigt. Nur diese Ebene kann entscheiden, ob ein IOCP erforderlich ist oder nicht.
Aaronaught
Aus ThreadPoolGründen des Interesses ist es jedoch nur das .NET , das Sie auf diese Weise einschränkt, wahrscheinlich weil sie den Entwicklern nicht vertraut haben, um es richtig zu machen. Der nicht verwaltete Windows-Thread-Pool verfügt über eine sehr ähnliche API, ermöglicht jedoch die Auswahl des Thread-Typs.
Aaronaught
2
E / A-Abschlussports (IOCP). Die Beschreibung von IOCP ist nicht ganz korrekt. In IOCP gibt es eine statische Anzahl von Arbeitsthreads, die abwechselnd an ALLEN ausstehenden Aufgaben arbeiten. Nicht zu verwechseln mit Thread-Pools, deren Größe fest oder dynamisch sein kann, ABER ein Thread pro Aufgabe - schrecklich skalierbar. Im Gegensatz zu ASYNC haben Sie nicht einen Thread pro Aufgabe. Ein IOCP-Thread arbeitet möglicherweise ein wenig an Aufgabe 1, wechselt dann zu Aufgabe 3, Aufgabe 2 und dann wieder zurück zu Aufgabe 1. Task-Sitzungsstatus werden gespeichert und zwischen Threads übergeben.
MickyD
1
Was ist mit Datenbankeinfügungen? Gibt es einen ASYNC SQL-Befehl (wie Execute)? Bei Datenbank-Einfügungen handelt es sich um die langsamste E / A-Operation (aufgrund von Sperren), und das Warten des Hauptthreads auf das Einfügen der Zeilen ist nur eine Verschwendung von CPU-Zyklen.
Ian Thompson
45

Laut Thomas Marquadt vom ASP.NET-Team bei Microsoft ist die Verwendung des ASP.NET-ThreadPools (QueueUserWorkItem) sicher.

Aus dem Artikel :

F) Wenn meine ASP.NET-Anwendung CLR-ThreadPool-Threads verwendet, verhungere ich dann nicht ASP.NET, das auch den CLR-ThreadPool zum Ausführen von Anforderungen verwendet? ..

A) Um es zusammenzufassen, machen Sie sich keine Sorgen über das Verhungern von ASP.NET-Threads. Wenn Sie der Meinung sind, dass hier ein Problem vorliegt, lassen Sie es mich wissen und wir kümmern uns darum.

F) Soll ich meine eigenen Threads erstellen (neuer Thread)? Wird dies für ASP.NET nicht besser sein, da es den CLR ThreadPool verwendet.

A) Bitte nicht. Oder anders ausgedrückt, nein !!! Wenn Sie wirklich schlau sind - viel schlauer als ich -, können Sie Ihre eigenen Threads erstellen. Andernfalls denken Sie nicht einmal darüber nach. Hier sind einige Gründe, warum Sie nicht häufig neue Threads erstellen sollten:

  1. Es ist im Vergleich zu QueueUserWorkItem sehr teuer ... Wenn Sie übrigens einen besseren ThreadPool als die CLRs schreiben können, empfehle ich Ihnen, sich bei Microsoft zu bewerben, da wir auf jeden Fall Leute wie Sie suchen!.
Tim P.
quelle
4

Websites sollten nicht um das Laichen von Threads herumgehen.

Normalerweise verschieben Sie diese Funktionalität in einen Windows-Dienst, mit dem Sie dann kommunizieren (ich verwende MSMQ, um mit ihnen zu sprechen).

- Bearbeiten

Ich habe hier eine Implementierung beschrieben: Warteschlangenbasierte Hintergrundverarbeitung in der ASP.NET MVC-Webanwendung

- Bearbeiten

Um zu erweitern, warum dies noch besser ist als nur Threads:

Mit MSMQ können Sie mit einem anderen Server kommunizieren. Sie können maschinenübergreifend in eine Warteschlange schreiben. Wenn Sie also aus irgendeinem Grund feststellen, dass Ihre Hintergrundaufgabe die Ressourcen des Hauptservers zu stark beansprucht, können Sie sie ganz einfach verschieben.

Außerdem können Sie jede Aufgabe, die Sie ausführen wollten, stapelweise verarbeiten (E-Mails senden / was auch immer).

Mittags Seide
quelle
4
Ich würde nicht zustimmen, dass diese pauschale Aussage immer wahr ist - insbesondere für unkritische Aufgaben. Das Erstellen eines Windows-Dienstes nur zum Zwecke der asynchronen Protokollierung scheint definitiv übertrieben. Außerdem ist diese Option nicht immer verfügbar (MSMQ und / oder ein Windows-Dienst können bereitgestellt werden).
Michael Hart
Sicher, aber es ist die "Standard" -Methode, um asynchrone Aufgaben von einer Website aus zu implementieren (Warteschlangenthema gegen einen anderen Prozess).
Mittag Seide
2
Nicht alle asynchronen Aufgaben werden gleich erstellt, weshalb beispielsweise in ASP.NET asynchrone Seiten vorhanden sind. Wenn ich ein Ergebnis von einem Remote-Webdienst abrufen möchte, um es anzuzeigen, werde ich dies nicht über MSMQ tun. In diesem Fall schreibe ich mit einem Remote-Post in ein Protokoll. Es passt nicht zum Problem, einen Windows-Dienst zu schreiben oder MSMQ dafür anzuschließen (und ich kann es auch nicht, da diese bestimmte App auf Azure ist).
Michael Hart
1
Bedenken Sie: Sie schreiben auf einen Remote-Host? Was ist, wenn dieser Host nicht erreichbar oder nicht erreichbar ist? Möchten Sie Ihr Schreiben erneut versuchen? Vielleicht wirst du, vielleicht wirst du nicht. Mit Ihrer Implementierung ist es schwierig, es erneut zu versuchen. Mit dem Service wird es ziemlich trivial. Ich weiß zu schätzen, dass Sie dies möglicherweise nicht können, und ich lasse jemanden auf die spezifischen Probleme beim Erstellen von Threads von Websites antworten [dh wenn Ihr Thread kein Hintergrund usw. war], aber ich skizziere das "Richtige" Weg, es zu tun. Ich bin mit Azure nicht vertraut, obwohl ich ec2 verwendet habe (Sie können ein Betriebssystem darauf installieren, also ist alles in Ordnung).
Mittag Seide
@ Silky, danke für die Kommentare. Ich hatte "unkritisch" gesagt, um diese schwerere (und dennoch haltbare) Lösung zu vermeiden. Ich habe die Frage geklärt, damit klar ist, dass ich nicht nach Best Practices für in der Warteschlange befindliche Arbeitselemente frage. Azure unterstützt diese Art von Szenario (es verfügt über einen eigenen Warteschlangenspeicher). Der Warteschlangenvorgang ist jedoch für die synchrone Protokollierung zu teuer, sodass ich ohnehin eine asynchrone Lösung benötige. In meinem Fall bin ich mir der Fallstricke eines Fehlers bewusst, aber ich werde keine weitere Infrastruktur hinzufügen, nur für den Fall, dass dieser bestimmte Protokollierungsanbieter ausfällt - ich habe auch andere Protokollierungsanbieter.
Michael Hart
4

Ich denke definitiv, dass die allgemeine Praxis für schnelles asynchrones Arbeiten mit niedriger Priorität in ASP.NET darin besteht, den .NET-Thread-Pool zu verwenden, insbesondere für Szenarien mit hohem Datenverkehr, da Ihre Ressourcen begrenzt werden sollen.

Außerdem ist die Implementierung von Threading ausgeblendet. Wenn Sie anfangen, Ihre eigenen Threads zu erzeugen, müssen Sie diese ebenfalls ordnungsgemäß verwalten. Ich sage nicht, dass du es nicht kannst, aber warum das Rad neu erfinden?

Wenn die Leistung zu einem Problem wird und Sie feststellen können, dass der Thread-Pool der begrenzende Faktor ist (und nicht Datenbankverbindungen, ausgehende Netzwerkverbindungen, Speicher, Seitenzeitüberschreitungen usw.), optimieren Sie die Thread-Pool-Konfiguration, um mehr Arbeitsthreads und Anforderungen in höheren Warteschlangen zu ermöglichen , etc.

Wenn Sie kein Leistungsproblem haben, ist die Auswahl neuer Threads zur Reduzierung von Konflikten mit der ASP.NET-Anforderungswarteschlange eine klassische vorzeitige Optimierung.

Im Idealfall müssten Sie jedoch keinen separaten Thread verwenden, um einen Protokollierungsvorgang auszuführen. Aktivieren Sie einfach den ursprünglichen Thread, um den Vorgang so schnell wie möglich abzuschließen. Hier kommen MSMQ und ein separater Consumer-Thread / -Prozess ins Spiel. Ich bin damit einverstanden, dass dies schwerer und aufwändiger zu implementieren ist, aber Sie brauchen hier wirklich die Langlebigkeit - die Volatilität einer gemeinsam genutzten In-Memory-Warteschlange wird ihre Begrüßung schnell erschöpfen.

Sam
quelle
2

Sie sollten QueueUserWorkItem verwenden und vermeiden, neue Threads zu erstellen, als würden Sie die Pest vermeiden. Stellen Sie sich für ein Bild, das erklärt, warum Sie ASP.NET nicht verhungern lassen, da es denselben ThreadPool verwendet, einen sehr erfahrenen Jongleur vor, der mit zwei Händen ein halbes Dutzend Bowlingstifte, Schwerter oder was auch immer im Flug hält. Um zu sehen, warum das Erstellen eigener Threads schlecht ist, stellen Sie sich vor, was in Seattle zur Hauptverkehrszeit passiert, wenn stark genutzte Einfahrtsrampen zur Autobahn es Fahrzeugen ermöglichen, sofort in den Verkehr einzusteigen, anstatt ein Licht zu verwenden und die Anzahl der Einfahrten alle paar Sekunden auf eins zu beschränken . Eine ausführliche Erklärung finden Sie unter folgendem Link:

http://blogs.msdn.com/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx

Danke, Thomas

Thomas
quelle
Dieser Link ist sehr nützlich, danke für diesen Thomas. Es würde mich interessieren, was Sie von der Antwort von @ Aaronaught halten.
Michael Hart
Ich stimme Aaronaught zu und sagte dasselbe in meinem Blog-Beitrag. Ich habe es so ausgedrückt: "Um diese Entscheidung zu vereinfachen, sollten Sie nur [zu einem anderen Thread] wechseln, wenn Sie den ASP.NET-Anforderungsthread ansonsten blockieren würden, während Sie nichts tun. Dies ist eine übermäßige Vereinfachung, aber ich versuche es um die Entscheidung einfach zu machen. " Mit anderen Worten, tun Sie dies nicht für nicht blockierende Rechenarbeiten, sondern für asynchrone Webdienstanforderungen an einen Remoteserver. Hören Sie Aaronaught! :)
Thomas
1

Dieser Artikel ist nicht korrekt. ASP.NET verfügt über einen eigenen Thread-Pool, verwaltete Worker-Threads, um ASP.NET-Anforderungen zu bearbeiten. Dieser Pool besteht normalerweise aus einigen hundert Threads und ist vom ThreadPool-Pool getrennt, bei dem es sich um ein kleineres Vielfaches von Prozessoren handelt.

Die Verwendung von ThreadPool in ASP.NET beeinträchtigt ASP.NET-Arbeitsthreads nicht. Die Verwendung von ThreadPool ist in Ordnung.

Es wäre auch akzeptabel, einen einzelnen Thread einzurichten, der nur zum Protokollieren von Nachrichten dient, und das Producer / Consumer-Muster zu verwenden, um Protokollnachrichten an diesen Thread zu übergeben. In diesem Fall sollten Sie, da der Thread eine lange Lebensdauer hat, einen einzelnen neuen Thread erstellen, um die Protokollierung auszuführen.

Die Verwendung eines neuen Threads für jede Nachricht ist definitiv übertrieben.

Eine andere Alternative, wenn Sie nur über die Protokollierung sprechen, ist die Verwendung einer Bibliothek wie log4net. Es behandelt die Anmeldung in einem separaten Thread und kümmert sich um alle Kontextprobleme, die in diesem Szenario auftreten können.

Samuel Neff
quelle
1
@Sam, ich verwende tatsächlich log4net und sehe nicht, dass Protokolle in einem separaten Thread geschrieben werden. Gibt es eine Option, die ich aktivieren muss?
Michael Hart
1

Ich würde sagen, der Artikel ist falsch. Wenn Sie einen großen .NET-Shop betreiben, können Sie den Pool sicher über mehrere Apps und mehrere Websites hinweg (unter Verwendung separater App-Pools) verwenden, einfach basierend auf einer Anweisung in der ThreadPool- Dokumentation:

Es gibt einen Thread-Pool pro Prozess. Der Thread-Pool hat eine Standardgröße von 250 Worker-Threads pro verfügbarem Prozessor und 1000 E / A-Abschluss-Threads. Die Anzahl der Threads im Thread-Pool kann mithilfe der SetMaxThreads-Methode geändert werden. Jeder Thread verwendet die Standardstapelgröße und wird mit der Standardpriorität ausgeführt.

Christopher Nobles
quelle
Eine Anwendung, die in einem einzigen Prozess ausgeführt wird, kann sich selbst zum Absturz bringen! (Oder zumindest die eigene Leistung genug herabsetzen, um den Thread-Pool zu einem Verlust zu machen.)
Jeff Sternal
Ich vermute also, dass ASP.NET-Anforderungen die E / A-Vervollständigungsthreads verwenden (im Gegensatz zu den Arbeitsthreads) - ist das richtig?
Michael Hart
Aus dem Artikel von Fritz Onion habe ich in meiner Antwort verlinkt: "Dieses Paradigma ändert [von IIS 5.0 zu IIS 6.0] die Art und Weise, wie Anforderungen in ASP.NET behandelt werden. Anstatt Anforderungen von inetinfo.exe an den ASP.NET-Arbeitsprozess zu senden, http. sys stellt jede Anforderung im entsprechenden Prozess direkt in die Warteschlange. Daher werden alle Anforderungen jetzt von Arbeitsthreads bearbeitet, die aus dem CLR-Thread-Pool stammen, und niemals von E / A-Threads . " (meine Betonung)
Jeff Sternal
Hmmm, ich bin mir immer noch nicht ganz sicher ... Dieser Artikel stammt aus dem Juni 2003. Wenn Sie diesen Artikel aus dem Mai 2004 lesen (zugegebenermaßen noch ziemlich alt), heißt es: "Die Sleep.aspx-Testseite kann verwendet werden, um einen ASP zu führen .NET I / O-Thread beschäftigt ", wobei Sleep.aspx nur bewirkt, dass der aktuell ausgeführte Thread in den Ruhezustand versetzt wird: msdn.microsoft.com/en-us/library/ms979194.aspx - Wenn ich eine Chance habe, werde ich sehen wenn ich dieses Beispiel codieren und unter IIS 7 und .NET 3.5 testen kann
Michael Hart
Ja, der Text dieses Absatzes ist verwirrend. Weiter unten in diesem Abschnitt wird auf ein Support-Thema verwiesen ( support.microsoft.com/default.aspx?scid=kb;EN-US;816829 ) verwiesen , das die Dinge verdeutlicht: Das Ausführen von Anforderungen in E / A-Abschluss-Threads war ein .NET Framework 1.0 Problem, das im Hotfix-Rollup-Paket von ASP.NET 1.1 vom Juni 2003 behoben wurde (danach "ALLE Anforderungen werden jetzt auf Worker-Threads ausgeführt"). Noch wichtiger ist, dass dieses Beispiel ziemlich deutlich zeigt, dass der ASP.NET-Thread-Pool der gleiche Thread-Pool ist, der von verfügbar gemacht wird System.Threading.ThreadPool.
Jeff Sternal
1

Letzte Woche wurde mir bei der Arbeit eine ähnliche Frage gestellt, und ich werde Ihnen die gleiche Antwort geben. Warum sind Sie Multithreading-Webanwendungen pro Anfrage? Ein Webserver ist ein fantastisches System, das stark optimiert wurde, um viele Anforderungen rechtzeitig bereitzustellen (dh Multithreading). Überlegen Sie, was passiert, wenn Sie fast jede Seite im Web anfordern.

  1. Für eine Seite wird eine Anfrage gestellt
  2. HTML wird zurück serviert
  3. Das HTML weist den Client an, weitere Anforderungen (js, css, images usw.) zu stellen.
  4. Weitere Informationen werden zurückgesandt

Sie geben das Beispiel der Remote-Protokollierung an, dies sollte jedoch ein Anliegen Ihres Loggers sein. Es sollte ein asynchroner Prozess vorhanden sein, um Nachrichten rechtzeitig zu empfangen. Sam weist sogar darauf hin, dass Ihr Logger (log4net) dies bereits unterstützen sollte.

Sam hat auch insofern Recht, als die Verwendung des Thread-Pools in der CLR keine Probleme mit dem Thread-Pool in IIS verursacht. Die Sache, mit der Sie sich hier befassen müssen, ist, dass Sie keine Threads aus einem Prozess erzeugen, sondern neue Threads aus IIS-Threadpool-Threads erzeugen. Es gibt einen Unterschied und die Unterscheidung ist wichtig.

Threads vs Prozess

Sowohl Threads als auch Prozesse sind Methoden zum Parallelisieren einer Anwendung. Prozesse sind jedoch unabhängige Ausführungseinheiten, die ihre eigenen Statusinformationen enthalten, ihre eigenen Adressräume verwenden und nur über Interprozesskommunikationsmechanismen (im Allgemeinen vom Betriebssystem verwaltet) miteinander interagieren. Anwendungen werden normalerweise während der Entwurfsphase in Prozesse unterteilt, und ein Masterprozess erzeugt explizit Unterprozesse, wenn es sinnvoll ist, wichtige Anwendungsfunktionen logisch zu trennen. Prozesse sind mit anderen Worten ein architektonisches Konstrukt.

Im Gegensatz dazu ist ein Thread ein Codierungskonstrukt, das die Architektur einer Anwendung nicht beeinflusst. Ein einzelner Prozess kann mehrere Threads enthalten. Alle Threads innerhalb eines Prozesses haben denselben Status und denselben Speicherplatz und können direkt miteinander kommunizieren, da sie dieselben Variablen verwenden.

Quelle

Ty.
quelle
3
@Ty, danke für die Eingabe, aber ich weiß genau, wie ein Webserver funktioniert, und er ist für die Frage nicht wirklich relevant. Wie ich bereits in der Frage sagte, bitte ich als Architekt nicht um Anleitung Problem. Ich bitte um spezifische technische Informationen. Was das "Anliegen des Loggers" betrifft, das bereits einen asynchronen Prozess haben sollte - wie sollte dieser asynchrone Prozess Ihrer Meinung nach von der Logger-Implementierung geschrieben werden?
Michael Hart
0

Ich bin mit dem Artikel, auf den verwiesen wird (C # feeds.com), nicht einverstanden. Es ist einfach, einen neuen Thread zu erstellen, aber gefährlich. Die optimale Anzahl aktiver Threads, die auf einem einzelnen Kern ausgeführt werden sollen, ist tatsächlich überraschend niedrig - weniger als 10. Es ist viel zu einfach, die Maschine Zeit damit zu verschwenden, Threads zu wechseln, wenn Threads für kleinere Aufgaben erstellt werden. Threads sind eine Ressource, die die Verwaltung erfordert. Die WorkItem-Abstraktion ist dafür da.

Hier besteht ein Kompromiss zwischen der Reduzierung der Anzahl der für Anforderungen verfügbaren Threads und der Erstellung zu vieler Threads, um eine effiziente Verarbeitung zu ermöglichen. Dies ist eine sehr dynamische Situation, aber ich denke, eine, die aktiv verwaltet werden sollte (in diesem Fall vom Thread-Pool), anstatt es dem Prozessor zu überlassen, der Erstellung von Threads voraus zu sein.

Schließlich macht der Artikel einige ziemlich umfassende Aussagen über die Gefahren der Verwendung des ThreadPool, aber es braucht wirklich etwas Konkretes, um sie zu sichern.

Timbo
quelle
0

Ob IIS denselben ThreadPool für die Verarbeitung eingehender Anforderungen verwendet oder nicht, scheint schwer zu beantworten zu sein und hat sich auch gegenüber den Versionen geändert. Es scheint daher eine gute Idee zu sein, ThreadPool-Threads nicht übermäßig zu verwenden, damit IIS viele davon zur Verfügung hat. Auf der anderen Seite scheint es eine schlechte Idee zu sein, für jede kleine Aufgabe einen eigenen Thread zu erstellen. Vermutlich haben Sie eine Art Sperre in Ihrer Protokollierung, sodass jeweils nur ein Thread fortschreiten kann, und der Rest wechselt sich nur ab, wenn er geplant und außerplanmäßig wird (ganz zu schweigen vom Aufwand für das Laichen eines neuen Threads). Im Wesentlichen stoßen Sie auf die genauen Probleme, die der ThreadPool vermeiden soll.

Es scheint ein vernünftiger Kompromiss für Ihre App zu sein, einen einzelnen Protokollierungsthread zuzuweisen, an den Sie Nachrichten weitergeben können. Sie sollten darauf achten, dass das Senden von Nachrichten so schnell wie möglich erfolgt, damit Sie Ihre App nicht verlangsamen.

bmm6o
quelle
0

Sie können Parallel.For oder Parallel.ForEach verwenden und die Grenze möglicher Threads definieren, die Sie zuweisen möchten, um einen reibungslosen Ablauf zu gewährleisten und das Verhungern des Pools zu verhindern.

Wenn Sie jedoch im Hintergrund ausgeführt werden, müssen Sie unten in der ASP.Net-Webanwendung den reinen TPL-Stil verwenden.

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
quelle