Laden...

Thread-Synchronisation mit mehreren Consumer-Threads

Erstellt von BrainBug22 vor 13 Jahren Letzter Beitrag vor 13 Jahren 1.888 Views
B
BrainBug22 Themenstarter:in
51 Beiträge seit 2008
vor 13 Jahren
Thread-Synchronisation mit mehreren Consumer-Threads

Hallo, ich habe folgendes Problem:

Ich muss viele Sachen gleichzeitig verarbeiten, darf das System jedoch nicht überlasten. Daher habe ich mir ein Konstrukt ausgedacht welches nicht 100% dem Producer/Consumer Konzept entspricht, aber recht ähnlich ist:

Ich habe einen Thread, ich nenn ihn mal Main-Thread, der zyklisch Anfragen erstellt und diese in einer globalen Liste platziert und ein ebenfalls globales AutoResetEvent (NewRequestEvent) setzt.

Außerdem erstelle ich beim Programmstart eine einstellbare Anzahl Arbeiter-Threads, die per WaitOne() auf NewRequestEvent warten. Kommt das Event, schnappt sich einer dieser Threads die älteste Anfrage aus der Liste ,arbeitet sie ab und stellt das Ergebnis in einer globalen Liste ab und setzt wiederum ein globales AutoResetEvent (NewResultEvent).

Sobald vom Main-Thread alle Anfragen des Zyklus generiert wurden, wartet dieser per WaitOne() auf NewResultEvent. Und zwar so lange bis die Ergebnisse aller Anfragen vorliegen.

Mein Problem ist, das nur eine Anfrage verarbeitet wird. Ich warte also auf die restlichen Ergebnisse, es wird aber nur eine Anfrage aus der Liste genommen und dann ist Ende.

Manch einer wird mir jetzt raten das mit Threadpool zu machen. Da pro Zyklus aber bis zu 50 Anfragen zu verarbeiten sind und diese relativ rechenintensiv sind, möchte ich ungerne soviele Threads wie Anfragen erstellen.

PS: Ich hatte das ganze schonmal am Laufen, indem ich statt der Events den Inhalt der Listen gepollt habe, dies führt jedoch zu einer enormen Belastung, da die ungenutzten Threads nicht schlafen, sondern permanent die Listen prüfen.

K
593 Beiträge seit 2007
vor 13 Jahren

Hallo Pegasus22,

dein Problem ist, soweit ich das sehe, eigentlich schon gelöst. Schau dir mal diese SyncQueue von herbivore an. In deinem Mainthread startest du soviele Threads wie du willst übergibst die Queue und füllst sie fröhlich mit Aufgaben. Immer wenn ein Thread nichts zu tun hat wartet er bis wieder was in der Queue ist.

Viele Grüße,

Kaji

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

wenn .net 4.0 verwendet wird/werden kann so ist die BlockingCollection<T> dafür gedacht.

Ein Beispiel hierzu findet sich in Patterns for Parallel Programming with the .NET Framework bzw. dem dort verlinkten PDF.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo Pegasus22,

deine Synchronisierung kann prinzipiell nicht funktionieren. So solltest deinen Weg nicht weiter verfolgen. Meine SyncQueue tut genau was du willst. Synchronisierung ist kein einfaches Geschäft. Die SyncQueue nimmt dir das alles ab. Wenn du unbedingt selbst synchronisiert willst, beachte meine Warnung in: 2 Threads - Receive/Send - nur einer wird ausgeführt.

herbivore

3.971 Beiträge seit 2006
vor 13 Jahren

Daher habe ich mir ein Konstrukt ausgedacht welches nicht 100% dem Producer/Consumer Konzept entspricht

So ein denken ist mist. Warum sollte ein Prozess, egal welcher Art, nicht die zur Verfügung stehenden Kapazitäten voll ausschöpfen dürfen? Auf Arbeit gibts du ja sicherlich auch ca./an die 100% und nicht 10% Montags, 20% Dienstags, etc.

Etwas anderes ist es, wenn ein Prozess nicht den kompletten Rechner blockieren/auslasten soll. Doch das geht in Windows einfacher und effizienter mit Prioritäten. Wenn du deinen Threads eine niedrige Priorität und zusätzlich dem Prozess niedrige Priorität zuweist, bekommen die einzelnen Threads nur Rechenzeit, wenn andere Threads gerade nicht ausgeführt werden.

Etwas kritischer siehts mit IO Zugriffen, denn dort gibts von Windows aus kein Scheduling.

globales AutoResetEvent

Sowas find ich persönlich auch nicht toll. Kapsel jeweils eine Queue und ein WaitHandle lieber in eine seperate Komponente und stecke die einzelnen Komponente in der Main-Methode zusammen. Später kannst du dann leicht(er) das ganze auf ein MultiCore-System umbauen und so das ganze hochskalieren.

Eventuell ist auch folgender Link was für dich:
Nach Reihenfolge geordnete Ausführung mit ThreadPool

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

G
46 Beiträge seit 2010
vor 13 Jahren

Hallo herbivore,

deine Synchronisierung kann prinzipiell nicht funktionieren

Woran liegt das? An den Fallstricken, die Du auch unter
Programmier-Spiel erwähnst?

Grüße

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo Gwar,

die Fallstricke zeigen erstmal nur, wie kompliziert gerade solche Producer/Consumer-Szenarien zu synchronisieren sind und auf was man alles aufpassen muss.

Ich sehe zwei Gründe, warum AutoResetEvents für ein solches Szenerio nicht geeignet sind. Zum einen sind AutoResetsEvents nicht die erste Wahl, wenn man exklusiven Zugriff herstellen will (und den braucht man ja für den Zugriff auf die gemeinsame unsynchronisierte Queue). Dazu gibt es Mutex oder einfach Monior/lock. Man kriegt zwar exklusiven Zugriff auch mir AutoResetsEvent hin, aber wie gesagt, ist es nicht erste Wahl. Was aber zum anderen noch wichtiger ist. Ein AutoResetsEvent merkt sich keine mehrfachen Signalisierungen. Wenn z.B. drei Threads vor der Ampel (=AutoResetsEvent) und ein anderer Thread schaltet die Ampel auf grün und gleich danach noch ein zweiter (mit der Absicht zwei von den drei wartenden Threads weiterlaufen zu lassen), dann läuft trotzdem nur ein Thread weiter. Nach dem guten alten Motto: grüner wirds nicht).

herbivore

B
BrainBug22 Themenstarter:in
51 Beiträge seit 2008
vor 13 Jahren

Vielen Dank für die vielen Anregungen!
Den exklusiven Zugriff auf die Listen habe ich natürlich per Mutex abgesichert. Nichts desto trotz bringt wohl das "grüner wirds nicht" der Events die Sache zu Fall sobald mehrere Threads im Spiel sind.
Ich werde mir die SyncQueue genauer anschauen, als auch die .NET 4 Geschichte.

Eine Frage habe ich noch zu Queues: Ich muss aufgrund zeitlich verzögerter Abarbeitung im Folgeprozess auch gegebenenfalls Elemente mitten aus der Liste entfernen um passende "Ergebnispaare" von verschiedenen Threads zu kombinieren. Geht das überhaupt mit einer Queue? Oder müsste ich dann die Queue in eine Liste umfüllen und dort durchsuchen?

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo Pegasus22,

Geht das überhaupt mit einer Queue?

das kommt darauf an, wie man eine Queue definiert. 😃 Was du willst, bekommt man sicher hin (man kann ja intern eine Liste statt einer echten Queue verwenden) und kann sowas auch als Klasse genauso kapseln wie die SyncQueue, aber dann muss man in dieser Klasse die Synchronisierung wieder wasserdicht machen, was sicher nicht trivial ist.

herbivore

3.971 Beiträge seit 2006
vor 13 Jahren

Mutexe werden in .NET nur für systemweite kritische Bereiche (Mutex mit Namen) gebraucht. Wenn du den Mutex nur brauchst um innerhalb deiner Anwendung kritische Bereiche zu schützen/definieren, benutze lieber lock, ist einfacher und vermutlich auch etwas fixer.

Queue:
Wenn du aus einer Queue mittendrin Elemente entfernen willst, solltest du dir eine eigenen Queue-Wrapper bauen und innerhalb eine LinkedList verwenden. Queue und List arbeiten intern mit Arrays, entfernst du ein Element aus der Mitte, muss das Array neu zusammenkopiert werden.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

B
BrainBug22 Themenstarter:in
51 Beiträge seit 2008
vor 13 Jahren

So, durch die Anregungen habe ich mir das .NET 4 Parrallel PDF durchgearbeitet und daraufhin den ganzen Kram über Bord geworfen. Statt den manuell angelegten Threads mit Anfrage und Ergebnis-Queues nutze ich jetzt einfach Parallel.ForEach. Und siehe da alles funktioniert super! Danke nochmal!
Die SyncQueue werde ich allerdings ebenfalls für andere Aufgaben im Hinterkopf halten 😉

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

habe ich mir das .NET 4 Parrallel PDF durchgearbeitet und daraufhin den ganzen Kram über Bord geworfen

Vergiss dabei aber auf keinem Fall dass dort die selben Grundlagen gelten! Sonst kannst du (irgendwann) eine Überraschung erleben.

Ich kann dir daher nur raten dass du dich trotzdem mit der SyncQueue beschäftigts - alleine für das Verständnis ist das hilfreich. .net 4.0 hat zwar den Anschein dass Parallelisierung zum Kinderspiel wird, aber dem ist nicht so.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"