Multithreading - Anzahl der Threads kontrollieren?

chmee

verstaubtes inventar
Premium-User
Folgendes Beispiel:
Code:
// gegeben: array aus dateinamen(bilder)
foreach(string picture in picturelist)
{
   effekt(picture,"schwarzweiss");
}
serielles Abarbeiten, ein Kern wird belastet. Angenommen, ich würde die Funktion effekt() als Thread definieren, müsste (der Logik wegen) diese Schleife x Threads eröffnen, für jedes Bild einen Einzelnen.
Code:
// gegeben: array aus dateinamen(bilder)
foreach(string picture in picturelist)
{
   ThreadStart sub = new ThreadStart(effekt(picture,"schwarzweiss"));
   Thread subThread = new Thread(sub);
   subThread.Start();
   // Thread in der Funktion wieder beenden?
}
Ist das eine sinnvolle Lösung oder ist es zu "unkontrolliert" und man sollte anfangs die Anzahl der Kerne auslesen und darauf hinarbeiten? Threads haben, wie ich gelesen hab, bei der Initialisierung doch einen gewissen Overhead an Platz und Zeit zu verbraten.

Kurzum, wie ist solch ein Beispiel zu optimieren?

mfg chmee
 

MCoder

Erfahrenes Mitglied
Hallo chmee ,

statt immer selbst einen neuen Thread zu erzeugen, könntest du die ThreadPool-Klasse verwenden, die (wie der Name schon sagt) einen Pool von Threads bereitstellt. Da die Threads schon existieren, entfällt der Overhead für ihre Initialisierung.

Gruß
MCoder
 

Shakie

Erfahrenes Mitglied
Parallel.ForEach benutzt aber keinen Threadpool, sondern teilt die Arbeitsliste in Blöcke auf, oder? (Wenn die Bearbeitungsdauer eines Bildes erheblich von Bild zu Bild variiert, dann ist ein Threadpool wohl sinnvoller.)
 

ComFreek

Mod | @comfreek
Moderator
Ist das eine sinnvolle Lösung oder ist es zu "unkontrolliert" und man sollte anfangs die Anzahl der Kerne auslesen und darauf hinarbeiten?
Zu C# kann ich dir nichts sagen, jedoch ist es so, dass wenn du mehr Threads erstellst, als der Prozessor virtuelle oder physische Kerne besitzt, dass die Leistungeverbesserungen dann wieder abnehmen.

Z.B. könnte eine CPU mit 4 Kernen auch nicht mehr 4 Threads gleichzeitig ausführen, das würde nur zur einer Leistungsminderung führen.
 

chmee

verstaubtes inventar
Premium-User
Ja, deswegen die Frage. Dennoch denke ich, eine nicht optimale parallele Ausführung ist schneller als das sequentielle Rechnen auf einem Kern. Mehr Gedanken hab ich mir um das Verschleudern von Speicherresoourcen pro Thread gemacht. Sind erstmal unwissenderweise 450 Threads geöffnet und jeder will ~5MB Speicher, könnte "das Gegenteil von schnell" eintreten.

Wie ich das verstehe, ist der Threadpool genau dafür gemacht, Threads queuen, bis Ressourcen frei werden. Ich habe es heute schon implementiert, läuft aber nicht so rund. Von 457 umzurechnenden Bildern landen nur 3(!) im fertig-Ordner und der Code wirft mir noch ne Exception, weil ich angeblich zweimal die gleiche Datei schreibe. Heute keine Lust mehr, Morgen in aller Ruhe.

Bis jetzt, Danke an Alle.

mfg chmee
 

chmee

verstaubtes inventar
Premium-User
Um mal ne Rückmeldung zu geben.

Code:
int Cores;
// --------------------
Cores = Environment.ProcessorCount;
Hiermit lese ich die Anzahl der Kerne aus und werde später die Anzahl für die gleichzeitigen Prozesse anwenden. Ob es wirklich nötig ist, ich glaube nicht. Andersrum kann ich durch das Setzen der Variable die Multithreads auf zB 1 begrenzen, um Effekte zu beobachten.

Das eigentliche Multithreading sah dann so aus:
C#:
ThreadPool.SetMaxThreads(Cores, Cores);
                    
// Countdownevent für das saubere Herunterzählen der offenen Threads
// vor Allem soll die Ausführung warten, bis alle Threads durch sind
var cde = new CountdownEvent(taskCount);
                    
// die eigentliche Schleife
for (var frame = 0; frame < 100; frame++)
{
   // objekt "daten" zur Übergabe an den Worker-Thread
   daten.frame = frame;
   daten.CDEvent = cde;
   ThreadPool.QueueUserWorkItem(new WaitCallback(_doFrame),daten);
}
cde.Wait();

private void _doFrame(object daten)
{
   // -- object "state" als Object DoFrameData verstehen
   DoFrameData param = new DoFrameData();
   param = (DoFrameData)daten;

   // mach was damit
   System.Diagnostics.Debug.WriteLine("Thread frame "+ param.frame.ToString());

   // wichtig, das Reset-Signal für den Countdown
   param.CDEvent.Signal();
}
Ob das nun perfekt ist, ich weiss es nicht, aber vielleicht hilft es Anderen auf dem Weg. Danke an Alle.

mfg chmee