2010-08-04 11 views
6

Ich erstellte eine App, die Arbeit an Tausenden von Dateien ausführt, dann schreibt geänderte Kopien dieser Dateien auf die Festplatte. Ich benutze einen ThreadPool, aber es hat so viele Threads erzeugt, dass der PC nicht mehr reagiert 260), also änderte ich den Max von 250 auf 50, das löste dieses Problem (App erzeugt nur etwa 60 Threads) Jetzt, da die Dateien so schnell fertig sind, bindet sie die Benutzeroberfläche an den Punkt, an dem der PC nicht mehr reagiert.Kann ich die I/O meiner C# -App beschränken

Gibt es eine Möglichkeit, die Anzahl der I/O zu begrenzen - ich meine, 50 Threads verwenden, um die Arbeit an den Dateien, aber nicht 50 Threads gleichzeitig zu schreiben, wenn sie verarbeitet werden. Ich würde das Schreiben des Dateiteils lieber nicht neu entwerfen, wenn ich mich davon zurückhalten kann - ich hatte gehofft, ich könnte die Menge an I/O begrenzen (gleichzeitig), die die Threads aus diesem Pool verbrauchen könnten.

+1

Sie sagen "die Dateien werden so schnell fertig, es bindet die Benutzeroberfläche". Wird die Benutzeroberfläche jedes Mal aktualisiert, wenn eine Datei bereit ist? Wenn ja, könnte das hier das eigentliche Problem sein? –

Antwort

4

Sie brauchen wirklich nicht so viele Threads. Eine Platte kann nur ihren maximalen Lese- und Schreibdurchsatz unterstützen, den ein einzelner Thread leicht ausschöpfen kann, wenn er für IO bestimmt ist, d. H. Lesen oder Schreiben. Sie können auch nicht gleichzeitig auf einer Festplatte lesen und schreiben (obwohl dies mit Betriebssystem-Caching-Layern usw. kompliziert ist), so dass das gleichzeitige Lesen und Schreiben von Threads sehr kontraproduktiv sein kann. Es ist auch wenig davon zu gewinnen, mehr Threads als Prozessoren/Kerne für Ihre Nicht-IO-Aufgaben zu haben, da alle zusätzlichen Threads einen Großteil ihrer Zeit darauf verwenden werden, auf einen Kern zu warten, z. Wenn Sie 50 Threads und 4 Kerne haben, sind mindestens 46 Threads zu jeder Zeit inaktiv. Die verschwendeten Threads, die zum Speicherverbrauch beitragen, verursachen auch einen Leistungs-Overhead, da sie alle kämpfen, um irgendwann auf einem Core einen Crack zu bekommen, und das Betriebssystem muss diesen Kampf vermitteln.

Ein einfacherer Ansatz wäre ein einzelner Thread, dessen Aufgabe es ist, in die Dateien zu lesen, und fügen Sie die Daten dann zu einer blockierenden Warteschlange (siehe zB ConcurrentQueue), inzwischen haben eine Reihe von Worker-Threads, die auf warten Datei Daten in der Warteschlange (zB eine Anzahl Threads gleich der Anzahl der Prozessoren \ Kerne). Diese Worker-Threads werden sich beim Hinzufügen von Elementen durch die Warteschlange bohren und blockieren, wenn sie leer sind. Wenn ein Worker-Thread eine Arbeit beendet, kann er diese zu einer anderen blockierenden Warteschlange hinzufügen, die entweder vom Leser-Thread oder einem dedizierten Writer-Thread überwacht wird. Seine Aufgabe ist es, die Dateien zu schreiben.

Dieses Muster versucht E/A und CPU unter einem viel kleineren Bündel kooperierender Threads auszugleichen, wobei die Anzahl der IO-Threads auf das beschränkt ist, was eine Festplatte physisch ermöglicht, und eine Anzahl von CPU-Worker-Threads sinnvoll für die Anzahl der Prozessoren \ Kerne, die Sie haben. Im Wesentlichen trennt es die IO- und CPU-Arbeit, so dass sich die Dinge vorhersehbarer verhalten.

Darüber hinaus, wenn IO wirklich das Problem ist (und nicht eine große Menge von Threads alle miteinander kämpfen), dann können Sie einige Pausen (zB Thread.Sleep) in Ihrer Datei lesen und schreiben Threads zu begrenzen, wie viel Arbeit, die sie tun.

aktualisieren

Vielleicht ist es wert zu erklären, warum es so viele Fäden sind in erster Linie erzeugt wird. Dies ist ein degenerativer Fall für die Verwendung von Threadpool und konzentriert sich auf das Einreihen von Arbeitselementen, die eine Komponente von IO enthalten.

Der Threadpool führt Arbeitsaufgaben aus der Warteschlange aus und überwacht, wie lange Arbeitsaufgaben ausgeführt werden. Wenn das Ausführen von Arbeitselementen eine lange Zeit in Anspruch nimmt (ich denke eine halbe Sekunde aus dem Speicher), wird es weitere Threads zum Pool hinzufügen, da dies der Ansicht ist, dass die Warteschlange dadurch schneller verarbeitet wird. Wenn die zusätzlichen gleichzeitigen Arbeitselemente jedoch auch eine Arbeits-IO für eine freigegebene Festplatte ausführen, wird sich die Leistung der Festplatte tatsächlich verringern, was bedeutet, dass Arbeitselemente noch länger zur Ausführung benötigen. Da Arbeitselemente länger ausgeführt werden, fügt der Threadpool weitere Threads hinzu. Dies ist der degenerative Fall, bei dem die Leistung immer schlechter wird, je mehr Threads hinzugefügt werden.

Die Verwendung eines Semaphors, wie vorgeschlagen, müsste sorgfältig durchgeführt werden, da der Semaphor das Blockieren von Threadpool-Threads verursachen könnte. Der Threadpool würde die Ausführung von Arbeitselementen lange dauern und weiterhin weitere Threads hinzufügen.

+0

Der ThreadPool führt komplexe Berechnungen basierend auf dem Inhalt jeder Datei durch und benutzt den ThreadPool, um diesen Teil des Prozesses zu beschleunigen :-) – schmoopy

+0

@schmoopy Ich bin mir nicht sicher, ob ich deinen Kommentar verstehe. Ich bin vertraut mit dem Threadpool und der Dateiverarbeitung, daher habe ich Ihre Frage beantwortet. Im Detail könnte ich hinzufügen ... –

Verwandte Themen