Wenn Sie viele logische Aufgaben haben, die eine ständige Verarbeitung erfordern, und diese parallel ausgeführt werden sollen, verwenden Sie den Pool + Scheduler.
Wenn Sie Ihre E / A-bezogenen Aufgaben gleichzeitig ausführen müssen, z. B. das Herunterladen von Daten von Remoteservern oder den Festplattenzugriff, dies jedoch alle paar Minuten tun müssen, erstellen Sie Ihre eigenen Threads und beenden Sie sie, sobald Sie fertig sind.
Bearbeiten: In Bezug auf einige Überlegungen verwende ich Thread-Pools für Datenbankzugriff, Physik / Simulation, KI (Spiele) und für Skriptaufgaben, die auf virtuellen Maschinen ausgeführt werden, die viele benutzerdefinierte Aufgaben verarbeiten.
Normalerweise besteht ein Pool aus 2 Threads pro Prozessor (heutzutage also wahrscheinlich 4). Sie können jedoch die Anzahl der gewünschten Threads festlegen, wenn Sie wissen, wie viele Sie benötigen.
Bearbeiten: Der Grund für das Erstellen eigener Threads liegt in Kontextänderungen (in diesem Fall müssen Threads zusammen mit ihrem Speicher in den Prozess ein- und ausgeblendet werden). Wenn Sie nutzlose Kontextänderungen haben, z. B. wenn Sie Ihre Threads nicht verwenden, können Sie die Leistung Ihres Programms leicht halbieren, wenn Sie sie nur herumliegen lassen (sagen wir, Sie haben 3 schlafende Threads und 2 aktive Threads). Wenn diese Download-Threads nur warten, verbrauchen sie Tonnen von CPU und kühlen den Cache für Ihre echte Anwendung ab
Ich würde vorschlagen, dass Sie aus den gleichen Gründen wie jede andere Sprache einen Thread-Pool in C # verwenden.
Verwenden Sie einen Thread-Pool, wenn Sie die Anzahl der ausgeführten Threads begrenzen oder den Aufwand für das Erstellen und Zerstören dieser Threads nicht erhöhen möchten.
Bei kleinen Aufgaben bedeutet das Buch, das Sie lesen, Aufgaben mit kurzer Lebensdauer. Wenn es zehn Sekunden dauert, um einen Thread zu erstellen, der nur eine Sekunde lang ausgeführt wird, sollten Sie an dieser Stelle Pools verwenden (ignorieren Sie meine tatsächlichen Zahlen, es ist das Verhältnis, das zählt).
Andernfalls verbringen Sie den größten Teil Ihrer Zeit damit, Threads zu erstellen und zu zerstören, anstatt nur die Arbeit zu erledigen, für die sie vorgesehen sind.
quelle
Hier ist eine schöne Zusammenfassung des Thread-Pools in .Net: http://blogs.msdn.com/pedram/archive/2007/08/05/dedicated-thread-or-a-threadpool-thread.aspx
Der Beitrag enthält auch einige Punkte, wann Sie den Thread-Pool nicht verwenden und stattdessen Ihren eigenen Thread starten sollten.
quelle
Ich empfehle dringend, dieses kostenlose E-Book zu lesen: Threading in C # von Joseph Albahari
Lesen Sie zumindest den Abschnitt "Erste Schritte". Das E-Book bietet eine großartige Einführung und enthält auch eine Fülle erweiterter Threading-Informationen.
Zu wissen, ob der Thread-Pool verwendet werden soll oder nicht, ist nur der Anfang. Als Nächstes müssen Sie bestimmen, welche Methode zum Eingeben des Thread-Pools Ihren Anforderungen am besten entspricht:
Dieses E-Book erklärt dies alles und gibt Ratschläge, wann Sie sie verwenden müssen, anstatt einen eigenen Thread zu erstellen.
quelle
Der Thread-Pool wurde entwickelt, um die Kontextumschaltung zwischen Ihren Threads zu reduzieren. Stellen Sie sich einen Prozess vor, in dem mehrere Komponenten ausgeführt werden. Jede dieser Komponenten kann Arbeitsthreads erstellen. Je mehr Threads sich in Ihrem Prozess befinden, desto mehr Zeit wird beim Kontextwechsel verschwendet.
Wenn nun jede dieser Komponenten Elemente in den Thread-Pool einreihen würde, wäre der Aufwand für die Kontextumschaltung wesentlich geringer.
Der Thread-Pool wurde entwickelt, um die Arbeit auf Ihren CPUs (oder CPU-Kernen) zu maximieren. Aus diesem Grund dreht der Thread-Pool standardmäßig mehrere Threads pro Prozessor.
In einigen Situationen möchten Sie den Thread-Pool nicht verwenden. Wenn Sie auf E / A oder auf ein Ereignis usw. warten, binden Sie diesen Thread-Pool-Thread zusammen und er kann von niemand anderem verwendet werden. Die gleiche Idee gilt für Aufgaben mit langer Laufzeit, obwohl das, was eine Aufgabe mit langer Laufzeit ausmacht, subjektiv ist.
Pax Diablo macht auch einen guten Punkt. Das Spinnen von Threads ist nicht kostenlos. Es braucht Zeit und sie verbrauchen zusätzlichen Speicher für ihren Stapelspeicher. Der Thread-Pool verwendet Threads erneut, um diese Kosten amortisieren zu können.
Hinweis: Sie haben nach der Verwendung eines Thread-Pool-Threads zum Herunterladen von Daten oder zum Ausführen von Festplatten-E / A gefragt. Sie sollten hierfür keinen Thread-Pool-Thread verwenden (aus den oben genannten Gründen). Verwenden Sie stattdessen asynchrone E / A (auch bekannt als BeginXX- und EndXX-Methoden). Für einen
FileStream
wäre dasBeginRead
undEndRead
. Für einenHttpWebRequest
wäre dasBeginGetResponse
undEndGetResponse
. Sie sind komplizierter zu verwenden, aber sie sind der richtige Weg, um Multithread-E / A durchzuführen.quelle
Achten Sie auf den .NET-Thread-Pool für Vorgänge, die möglicherweise einen signifikanten, variablen oder unbekannten Teil ihrer Verarbeitung blockieren, da dieser anfällig für Thread-Hunger ist. Erwägen Sie die Verwendung der parallelen .NET-Erweiterungen, die eine gute Anzahl logischer Abstraktionen über Thread-Operationen bereitstellen. Sie enthalten auch einen neuen Scheduler, der eine Verbesserung von ThreadPool darstellen sollte. Siehe hier
quelle
Ein Grund, den Thread-Pool nur für kleine Aufgaben zu verwenden, besteht darin, dass nur eine begrenzte Anzahl von Thread-Pool-Threads vorhanden ist. Wenn einer für eine lange Zeit verwendet wird, wird verhindert, dass dieser Thread von anderem Code verwendet wird. Wenn dies häufig vorkommt, kann der Thread-Pool aufgebraucht sein.
Die Verwendung des Thread-Pools kann subtile Auswirkungen haben. Einige .NET-Timer verwenden Thread-Pool-Threads und werden beispielsweise nicht ausgelöst.
quelle
Wenn Sie eine Hintergrundaufgabe haben, die lange anhält, beispielsweise für die gesamte Lebensdauer Ihrer Anwendung, ist es sinnvoll, einen eigenen Thread zu erstellen. Wenn Sie kurze Jobs haben, die in einem Thread ausgeführt werden müssen, verwenden Sie Thread-Pooling.
In einer Anwendung, in der Sie viele Threads erstellen, wird der Aufwand für das Erstellen der Threads erheblich. Durch die Verwendung des Thread-Pools werden die Threads einmal erstellt und wiederverwendet, wodurch der Aufwand für die Thread-Erstellung vermieden wird.
In einer Anwendung, an der ich gearbeitet habe, hat der Wechsel von der Erstellung von Threads zur Verwendung des Thread-Pools für die kurzlebigen Threads den Durchsatz der Anwendung erheblich verbessert.
quelle
Um die höchste Leistung bei gleichzeitig ausgeführten Einheiten zu erzielen, schreiben Sie Ihren eigenen Thread-Pool, in dem beim Start ein Pool von Thread-Objekten erstellt wird, und blockieren Sie (früher angehalten), bis ein Kontext ausgeführt wird (ein Objekt mit einer von implementierten Standardschnittstelle) dein Code).
So viele Artikel über Aufgaben vs. Threads vs. .NET ThreadPool bieten Ihnen nicht wirklich das, was Sie benötigen, um eine Entscheidung für die Leistung zu treffen. Aber wenn Sie sie vergleichen, gewinnen Threads und insbesondere ein Pool von Threads. Sie sind am besten auf die CPUs verteilt und starten schneller.
Was diskutiert werden sollte, ist die Tatsache, dass die Hauptausführungseinheit von Windows (einschließlich Windows 10) ein Thread ist und der Aufwand für das Umschalten des Betriebssystemkontexts normalerweise vernachlässigbar ist. Einfach ausgedrückt, ich konnte für viele dieser Artikel keine überzeugenden Beweise finden, unabhängig davon, ob der Artikel eine höhere Leistung durch Einsparen von Kontextwechsel oder eine bessere CPU-Auslastung beansprucht.
Nun zu ein bisschen Realismus:
Die meisten von uns brauchen keine deterministische Anwendung, und die meisten von uns haben keinen harten Hintergrund mit Threads, was beispielsweise häufig mit der Entwicklung eines Betriebssystems einhergeht. Was ich oben geschrieben habe, ist nichts für Anfänger.
Am wichtigsten ist es also zu diskutieren, was einfach zu programmieren ist.
Wenn Sie Ihren eigenen Thread-Pool erstellen, müssen Sie ein wenig schreiben, da Sie sich mit der Verfolgung des Ausführungsstatus, der Simulation des Suspendierens und Fortsetzens und dem Abbrechen der Ausführung befassen müssen - auch in einer anwendungsweiten Ausführung Herunterfahren. Möglicherweise müssen Sie sich auch Gedanken darüber machen, ob Sie Ihren Pool dynamisch vergrößern möchten und welche Kapazitätsbeschränkungen Ihr Pool haben wird. Ich kann ein solches Framework in einer Stunde schreiben, aber das liegt daran, dass ich es so oft gemacht habe.
Der einfachste Weg, eine Ausführungseinheit zu schreiben, ist möglicherweise die Verwendung einer Aufgabe. Das Schöne an einer Aufgabe ist, dass Sie eine erstellen und in Ihrem Code inline starten können (obwohl Vorsicht geboten sein kann). Sie können ein Stornierungs-Token übergeben, um es zu bearbeiten, wenn Sie die Aufgabe abbrechen möchten. Außerdem wird der Versprechungsansatz zum Verketten von Ereignissen verwendet, und Sie können einen bestimmten Werttyp zurückgeben. Darüber hinaus gibt es mit Async und Warten mehr Optionen und Ihr Code wird portabler.
Im Wesentlichen ist es wichtig, die Vor- und Nachteile von Tasks vs. Threads vs. .NET ThreadPool zu verstehen. Wenn ich hohe Leistung benötige, verwende ich Threads und bevorzuge die Verwendung meines eigenen Pools.
Eine einfache Möglichkeit zum Vergleichen besteht darin, 512 Threads, 512 Tasks und 512 ThreadPool-Threads zu starten. Am Anfang von Threads tritt eine Verzögerung auf (daher sollte ein Thread-Pool geschrieben werden), aber alle 512 Threads werden in wenigen Sekunden ausgeführt, während der Start von Tasks- und .NET ThreadPool-Threads bis zu einigen Minuten dauert.
Nachfolgend finden Sie die Ergebnisse eines solchen Tests (i5 Quad Core mit 16 GB RAM), bei dem alle 30 Sekunden ausgeführt werden. Der ausgeführte Code führt eine einfache Datei-E / A auf einem SSD-Laufwerk aus.
Testergebnisse
quelle
Thread-Pools eignen sich hervorragend, wenn Sie mehr Aufgaben als verfügbare Threads verarbeiten müssen.
Sie können alle Aufgaben zu einem Thread-Pool hinzufügen und die maximale Anzahl von Threads angeben, die zu einem bestimmten Zeitpunkt ausgeführt werden können.
Schauen Sie sich diese Seite auf MSDN an: http://msdn.microsoft.com/en-us/library/3dasc8as(VS.80).aspx
quelle
Verwenden Sie immer einen Thread-Pool, wenn Sie können, und arbeiten Sie auf der höchstmöglichen Abstraktionsebene. Thread-Pools verbergen das Erstellen und Zerstören von Threads für Sie. Dies ist normalerweise eine gute Sache!
quelle
Meistens können Sie den Pool verwenden, da Sie den teuren Prozess zum Erstellen des Threads vermeiden.
In einigen Szenarien möchten Sie jedoch möglicherweise einen Thread erstellen. Zum Beispiel, wenn Sie nicht der einzige sind, der den Thread-Pool verwendet, und der von Ihnen erstellte Thread eine lange Lebensdauer hat (um den Verbrauch gemeinsam genutzter Ressourcen zu vermeiden) oder wenn Sie beispielsweise die Stapelgröße des Threads steuern möchten.
quelle
Vergessen Sie nicht, den Hintergrundarbeiter zu untersuchen.
Ich finde für viele Situationen, es gibt mir genau das, was ich will, ohne das schwere Heben.
Prost.
quelle
Normalerweise benutze ich den Threadpool immer dann, wenn ich etwas in einem anderen Thread tun muss, und es ist mir egal, wann er ausgeführt wird oder endet. So etwas wie das Protokollieren oder vielleicht sogar das Herunterladen einer Datei im Hintergrund (obwohl es bessere Möglichkeiten gibt, dies asynchron zu tun). Ich benutze meinen eigenen Thread, wenn ich mehr Kontrolle brauche. Ich habe auch festgestellt, dass die Verwendung einer Threadsafe-Warteschlange (hacke deine eigene) zum Speichern von "Befehlsobjekten" gut ist, wenn ich mehrere Befehle habe, an denen ich in> 1 Thread arbeiten muss. Sie können also eine XML-Datei aufteilen und jedes Element in eine Warteschlange stellen. Anschließend arbeiten mehrere Threads an der Verarbeitung dieser Elemente. Ich habe vor langer Zeit eine solche Warteschlange in uni (VB.net!) Geschrieben, dass ich zu C # konvertiert habe. Ich habe es ohne besonderen Grund unten aufgeführt (dieser Code kann einige Fehler enthalten).
quelle
Ich wollte einen Thread-Pool, um die Arbeit mit möglichst geringer Latenz auf die Kerne zu verteilen, und das musste mit anderen Anwendungen nicht gut funktionieren. Ich stellte fest, dass die Leistung des .NET-Threadpools nicht so gut war, wie sie sein könnte. Ich wusste, dass ich einen Thread pro Kern haben wollte, also schrieb ich meine eigene Thread-Pool-Ersatzklasse. Der Code wird als Antwort auf eine andere Frage Stackoverflow bereitgestellt hier .
In Bezug auf die ursprüngliche Frage ist der Thread-Pool nützlich, um sich wiederholende Berechnungen in Teile aufzuteilen, die parallel ausgeführt werden können (vorausgesetzt, sie können parallel ausgeführt werden, ohne das Ergebnis zu ändern). Die manuelle Thread-Verwaltung ist nützlich für Aufgaben wie UI und IO.
quelle