Ist es ein Leistungseinbruch, Threads zu erstellen, die viele Schleifen durchlaufen, um nach Dingen zu suchen?

8

Dies ist eine allgemeine Frage, die ich mich immer gefragt habe.

Ist es für eine CPU im Allgemeinen intensiv, Threads zu erstellen, die "while not true ..." - Schleifen oder ähnliches ausführen?

Angenommen, ich mache:

// pseudo-code
new Thread(function() {
    while (!useHasDoneSomething) {
        // do nothing...
    }
    alert("you did something!");
}

Es ist also nur ein Teil des Codes, der in einem Thread ausgeführt wird, der darauf wartet, dass etwas passiert.

Aus irgendeinem Grund stelle ich mir vor, dass dies die CPU unter Druck setzen würde. Obwohl es sich nur um eine kleine Schleife handelt, wird die Schleife in dem Moment erneut ausgeführt, in dem ein Zyklus abgeschlossen ist. Würde das nicht bedeuten, dass es den Zyklus viele Male pro Millisekunde durchläuft? ... pro Nanosekunde!?

Das Betriebssystem "behandelt" das Programm und die Threads, oder? Aber das ist immer noch eine Menge Aufmerksamkeit für das Ausführen einer Schleife. Wenn die CPU nichts anderes zu tun hat, würde sie sich dann nicht einfach auf diese Schleife konzentrieren und dabei die gesamte Leerlaufzeit verbrauchen ?

Was ist, wenn ich 1000 Threads erstelle, die alle dieselbe Schleife ausführen? Wie könnte sich dies auf die Leistung / CPU-Zyklen auswirken?

Funktionieren Ereignisse auf diese Weise auch intern? Wurde ein Thread erstellt, der Schleifen erstellt, wenn Sie auf ein Ereignis in Java, .NET, Javascript usw. warten (z. B. durch Drücken einer Taste oder Ändern der Fenstergröße)?

Rowan Freeman
quelle
1
Was sagt Ihnen der Profiler?
Arseni Mourzenko
2
Meine Frage ist allgemein. Nicht so sehr eine Frage der Zahlen, sondern mehr "Was ist falsch an meinem Verständnis?"
Rowan Freeman
Ich habs. +1 dann.
Arseni Mourzenko

Antworten:

12

Ist es für eine CPU im Allgemeinen intensiv, Threads zu erstellen, die "while not true ..." - Schleifen oder ähnliches ausführen?

Ja. Die Verwendung einer solchen Schleife wird als beschäftigtes Warten bezeichnet, und dies wird aus einem bestimmten Grund so genannt. Diese Besetztschleifen überprüfen den Zustand so oft und so schnell, wie der Prozessor ihn verwalten kann. Wenn Sie also lange genug in der Schleife bleiben, steigt die Prozessorlast zuverlässig auf 100%.

Was ist, wenn ich 1000 Threads erstelle, die alle dieselbe Schleife ausführen? Wie könnte sich dies auf die Leistung / CPU-Zyklen auswirken?

Es schafft nur mehr Arbeit für die CPU, die Notizen macht.

Funktionieren Ereignisse auf diese Weise auch intern? Wurde ein Thread erstellt, der Schleifen erstellt, wenn Sie auf ein Ereignis in Java, .NET, Javascript usw. warten (z. B. durch Drücken einer Taste oder Ändern der Fenstergröße)?

Nein. Busy Loops sind eine Form der Abfrage und auch eine sehr ineffiziente. Betriebssysteme verfügen über andere Mechanismen, um zu erkennen, dass ein Ereignis aufgetreten ist (z. B. ein Interrupt oder ein bestimmter Aufruf), und sie verfügen über Mechanismen, mit denen ein Thread darauf warten kann, ohne CPU-Zeit zu verbrauchen. Diese Mechanismen werden auch verwendet, um die Ereignismechanismen von Sprachen wie Java zu implementieren.

Bart van Ingen Schenau
quelle
Vielen Dank! Was ist, wenn Sie den Thread während der Schleife für kurze Zeit, möglicherweise 100 Millisekunden, schlafen lassen? Würde dies den Thread so verlangsamen, dass CPU-Zyklen nicht so oft verschwendet würden und sich die Leistung erholen würde?
Rowan Freeman
1
@RowanFreeman: Es verringert die Belastung des Prozessors, ist aber immer noch höher als nur darauf zu warten, dass das Betriebssystem Ihnen mitteilt, dass X passiert ist.
Bart van Ingen Schenau
9

Dein Instinkt ist richtig. So beschäftigt zu warten wird beschrieben als

Das Spinnen kann unter bestimmten Umständen eine gültige Strategie sein, insbesondere bei der Implementierung von Spinlocks in Betriebssystemen, die für die Ausführung auf SMP-Systemen ausgelegt sind. Im Allgemeinen wird das Drehen jedoch als Anti-Pattern angesehen und sollte vermieden werden, da Prozessorzeit, die zum Ausführen einer anderen Aufgabe verwendet werden könnte, stattdessen für nutzlose Aktivitäten verschwendet wird.

Stattdessen sollten Sie ein Grundelement für die Parallelitätssteuerung in Betracht ziehen, das es dem Thread ermöglicht, wirklich inaktiv zu werden, bis er nützliche Arbeit zu erledigen hat, z. B. ein Semaphor oder eine Bedingungsvariable :

Für viele Anwendungen reicht ein gegenseitiger Ausschluss nicht aus. Threads, die eine Operation versuchen, müssen möglicherweise warten, bis eine Bedingung erfüllt ist. Eine geschäftige Warteschleife, wie

while not( condition ) do 
   // nothing
end

funktioniert nicht, da durch gegenseitigen Ausschluss verhindert wird, dass andere Threads in den Monitor gelangen, um die Bedingung zu erfüllen. Andere "Lösungen" existieren. B. eine Schleife, die den Monitor entsperrt, eine bestimmte Zeit wartet, den Monitor sperrt und auf den Zustand P prüft. Theoretisch funktioniert dies und blockiert nicht, aber es treten Probleme auf. Es ist schwer, eine angemessene Wartezeit zu bestimmen, die zu klein ist, und der Thread belastet die CPU, ist zu groß und reagiert anscheinend nicht mehr. Was benötigt wird, ist eine Möglichkeit, den Thread zu signalisieren, wenn die Bedingung P wahr ist (oder wahr sein könnte). Die Lösung sind Bedingungsvariablen. Konzeptionell ist eine Bedingungsvariable eine Warteschlange von Threads, die einem Monitor zugeordnet sind und in der ein Thread möglicherweise darauf wartet, dass eine Bedingung erfüllt wird. Somit ist jede Bedingungsvariable einer Zusicherung zugeordnet. Während ein Thread auf eine Bedingungsvariable wartet, wird nicht davon ausgegangen, dass dieser Thread den Monitor belegt. Daher können andere Threads in den Monitor eintreten, um den Status des Monitors zu ändern. Bei den meisten Monitortypen signalisieren diese anderen Threads möglicherweise die Bedingungsvariable, um anzuzeigen, dass die Zusicherung im aktuellen Status wahr ist.

Steven Schlansker
quelle
Ist also damit beschäftigt, auf einen Performance-Hit zu warten? Ist eine einzelne Schleife (obwohl sie ständig feuert) überhaupt ein Problem?
Rowan Freeman
1
@ RowanFreeman Es ist. Das geschäftige Warten brennt, wie der Text andeutet, weiterhin CPU-Zyklen nach nutzlosen Anweisungen ( dh wiederholt sich if (!useHasDoneSomething)immer wieder, so schnell es geht). Selbst wenn es Ihnen in Ihrem Programm nichts ausmacht, verschwendet es Zeit, die andere Programme zum Ausführen verwenden könnten. Aber selbst in Ihrem Programm verschwendet ein vielbeschäftigter Kellner Zeit, die einem Arbeitsthread zugewiesen werden könnte (falls nicht genügend Kerne verfügbar sind, damit alle Threads gleichzeitig ausgeführt werden können).
Afsantos
2
@afsantos Es gibt gültige Verwendungszwecke für beschäftigtes Warten - Alternativen bedeuten normalerweise, dass Sie Ihren Thread deaktivieren und später neu planen. Wenn Sie wissen, dass Sie nicht länger als ein paar CPU-Zyklen durchlaufen, kann es viel effizienter sein, zu warten, als einen Kontextwechsel zuzulassen.
Assylias
1
@assylias In der Tat kann ein Kontextwechsel eher ein Leistungseinbruch sein, wenn bekannt ist, dass die Wartezeit sehr kurz ist. Aus meiner Erfahrung heraus sind jedoch vielbeschäftigte Wartezeiten meistens unangemessen. Das OP müsste seinen Einzelfall bewerten, um festzustellen, ob es sich lohnt oder nicht.
Afsantos
2

Threads sollten auf eingehende Ereignisse warten, nicht auf Variablenänderungen.

Eingehende Ereignisse sind Semaphore, die verfügbar werden, Nachrichtenwarteschlangen werden nicht leer oder die Zeit vergeht:

semGet()
msqRead()
sleep()

Aus Thread-Sicht blockieren diese Funktionsaufrufe: Der Thread wartet, bis das Ereignis eingetreten ist, und überlässt die CPU-Last anderen Threads.

Aus Systemsicht ist der Thread als "Warten auf Ereignis" markiert und wird nicht geplant, solange das Ereignis nicht eingetreten ist.

Hardwareereignisse werden über Interrupts behandelt, die elektrische Signale an die CPU sind und die Ausführung von ISR (Interrupt Service Routines) auslösen, deren Aufgabe es ist, ein Softwareereignis auf Semaphoren, Nachrichtenwarteschlangen oder Zeit zu erzeugen.

Mouviciel
quelle
0

Der //do nothing Teil im normalen Zustand ist WAIT auf einem Semaphor oder mit anderen Worten SLEEP. Schlaf, bis dich eine Unterbrechung geweckt hat. Und daher geht der Thread in den "Ruhezustand" oder sagen wir einfach "nicht laufender Zustand". Nur die Prozesse, die sich im laufenden Zustand befinden, verbrauchen CPU. Der Ruhezustandsprozess verbraucht keine CPU. Sie verbrauchen möglicherweise Speicher, aber dieser Speicher wird intern vom virtuellen Speichersystem ausgelagert. Selbst wenn Sie 1000 solcher Threads erstellen, stellen diese keine große Bedrohung für Ihr System dar.

Manoj R.
quelle