Ich suche eine schnelle Art und Weise zu lassen viele Worker-Threads warten auf ein Ereignis fortzusetzen und blockieren den Haupt-Thread, bis alle Worker-Threads sind fertig zu lassen. Ich habe zuerst TPL oder AutoResetEvent verwendet, aber da meine Berechnung nicht so teuer ist, war der Aufwand viel zu hoch.beste Weg, um viele Arbeiter-Threads warten Mainthread und umgekehrt
Ich habe eine ziemlich interessante article zu diesem Problem gefunden und gute Ergebnisse (mit nur einem Worker-Thread) mit der letzten Synchronisationslösung (Interlocked.CompareExchange). Aber ich weiß nicht, wie ich es für ein Szenario verwenden soll, in dem viele Threads wiederholt auf ein Hauptprofil warten.
Hier ist ein Beispiel unter Verwendung von einzelnen Thread, CompareExchange und Barrier:
static void Main(string[] args)
{
int cnt = 1000000;
var stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < cnt; i++) { }
Console.WriteLine($"Single thread: {stopwatch.Elapsed.TotalSeconds}s");
var run = true;
Task task;
stopwatch.Restart();
int interlock = 0;
task = Task.Run(() =>
{
while (run)
{
while (Interlocked.CompareExchange(ref interlock, 0, 1) != 1) { Thread.Sleep(0); }
interlock = 2;
}
Console.WriteLine($"CompareExchange synced: {stopwatch.Elapsed.TotalSeconds}s");
});
for (int i = 0; i < cnt; i++)
{
interlock = 1;
while (Interlocked.CompareExchange(ref interlock, 0, 2) != 2) { Thread.Sleep(0); }
}
run = false;
interlock = 1;
task.Wait();
run = true;
var barrier = new Barrier(2);
stopwatch.Restart();
task = Task.Run(() =>
{
while (run) { barrier.SignalAndWait(); }
Console.WriteLine($"Barrier synced: {stopwatch.Elapsed.TotalSeconds}s");
});
for (int i = 0; i < cnt; i++) { barrier.SignalAndWait(); }
Thread.Sleep(0);
run = false;
if (barrier.ParticipantsRemaining == 1) { barrier.SignalAndWait(); }
task.Wait();
Console.ReadKey();
}
Durchschnittliche Ergebnisse (in Sekunden) sind:
Einfaden: 0,002 CompareExchange: 0,4 Barrier: 1
, 7Wie Sie Barriers' Overhead sehen scheint arround 4-mal höher zu sein! Wenn jemand mir das CompareExchange-Szenario wieder zusammenstellen kann, um mit mehreren Worker-Threads zu arbeiten, würde dies sicherlich auch helfen!
Sicher, 1 Sekunde Aufwand für eine Million Berechnungen ist ziemlich weniger! Eigentlich interessiert es mich nur.
Edit:
System.Threading.Barrier scheint die schnellste Lösung für dieses Szenario zu sein. Für eine doppelte Sperrspar (alle Arbeiter bereit für die Arbeit, alle workes fertig) habe ich den folgenden Code für die besten Ergebnisse:
while(work)
{
while (barrier.ParticipantsRemaining > 1) { Thread.Sleep(0); }
//Set work package
barrier.SignalAndWait()
}
Dieses Testprogramm sagt Ihnen leider nicht viel, weil die Threads keine Arbeit machen. Sie sollten ihnen eine Art von Last hinzufügen - vielleicht rufen Sie eine Methode auf, die einige Berechnungen mit Fließkomma ausführt. –
Misst dieser Code nicht den fast reinen Synchronisationsaufwand? Sicher, das Hinzufügen von Berechnungen wird die Ergebnisse mehr und mehr gleich machen. – user1039407
Es ist sehr schwierig Multithread-Sachen zu vergleichen, wenn Threads nicht blockieren, weil man immer den "glücklichen Pfad" festlegt. Aber sicher werden Sie nichts schneller als "Interlocked.CompareExchange()" bekommen, aber es ist irgendwie irrelevant, da Sie das nicht für mehr als 2 Threads verwenden können. –