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).
quelle
Antworten:
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 ArbeitsthreadsThreadPool
, die aufQueueUserWorkItem
Anforderungen reagieren .Ja, es stimmt, Sie können das
ThreadPool
in 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 verwendetQueueUserWorkItem
wird, 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
Begin
undEnd
. Wie inStream.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 HintergrundinstanzThread
in 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 verwendenThreadPool
oder 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.
quelle
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.ThreadPool
Grü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.Laut Thomas Marquadt vom ASP.NET-Team bei Microsoft ist die Verwendung des ASP.NET-ThreadPools (QueueUserWorkItem) sicher.
Aus dem Artikel :
quelle
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).
quelle
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.
quelle
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
quelle
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.
quelle
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:
quelle
System.Threading.ThreadPool
.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.
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.
Quelle
quelle
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.
quelle
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.
quelle
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.
quelle