web-dev-qa-db-de.com

Spinlock gegen Semaphor

Was sind die grundlegenden Unterschiede zwischen einem Semaphor und einem Spin-Lock?

Wann würden wir ein Semaphor über einem Spin-Lock verwenden?

109
iankits

Spinlock und Semaphor unterscheiden sich hauptsächlich in vier Punkten:

1. Was sie sind
Ein Spinlock ist eine mögliche Implementierung einer Sperre, nämlich eine, die durch Besetztwarten ("Drehen") implementiert wird. Ein Semaphor ist eine Verallgemeinerung einer Sperre (oder umgekehrt, eine Sperre ist ein Sonderfall einer Semaphor). Normalerweise , aber nicht notwendigerweise , sind Spinlocks nur innerhalb eines Prozesses gültig, während Semaphoren auch zum Synchronisieren zwischen verschiedenen Prozessen verwendet werden können.

Eine Sperre dient dem gegenseitigen Ausschluss. Das heißt, dass eins Thread gleichzeitig die Sperre abrufen und mit einem "kritischen Codeabschnitt" fortfahren kann. Normalerweise bedeutet dies Code, der einige von mehreren Threads gemeinsam genutzte Daten ändert.
Ein Semaphor hat einen Zähler und lässt sich von einem oder mehreren Threads abrufen, je nachdem was Wert, den Sie dort posten, und (in einigen Implementierungen) abhängig davon, wie hoch sein maximal zulässiger Wert ist.

Insofern kann man eine Sperre als Sonderfall eines Semaphors mit einem Maximalwert von 1 betrachten.

2. Was sie tun
Wie oben erwähnt, ist ein Spinlock ein Schloss und daher ein Mechanismus zum gegenseitigen Ausschluss (streng 1 zu 1). Dies funktioniert durch wiederholtes Abfragen und/oder Ändern eines Speicherorts, normalerweise auf atomare Weise. Dies bedeutet, dass das Erlangen eines Spinlocks eine "ausgelastete" Operation ist, die möglicherweise lange (möglicherweise für immer!) CPU-Zyklen verbrennt, während sie effektiv "nichts" bewirkt.
Der Hauptanreiz für einen solchen Ansatz ist die Tatsache, dass ein Kontextwechsel einen Overhead hat, der dem Drehen von ein paar hundert (oder vielleicht tausend) Malen entspricht. Wenn eine Sperre also durch Brennen von ein paar sich drehenden Zyklen erhalten werden kann, ist dies der Fall kann insgesamt sehr gut effizienter sein. Für Echtzeitanwendungen ist es möglicherweise nicht akzeptabel, zu blockieren und darauf zu warten, dass der Scheduler zu einem späteren Zeitpunkt zu ihnen zurückkehrt.

Ein Semaphor hingegen dreht sich entweder gar nicht oder nur für eine sehr kurze Zeit (als Optimierung zur Vermeidung des Systemaufwands). Wenn ein Semaphor nicht erfasst werden kann, wird es blockiert, und die CPU-Zeit wird an einen anderen Thread vergeben, der zur Ausführung bereit ist. Dies kann natürlich bedeuten, dass einige Millisekunden vergehen, bevor der Thread erneut geplant wird. Wenn dies jedoch kein Problem darstellt (normalerweise nicht), kann dies ein sehr effizienter, CPU-schonender Ansatz sein.

. Wie sie sich bei Überlastung verhalten
Es ist ein weit verbreitetes Missverständnis, dass Spinlocks oder Algorithmen ohne Sperre "im Allgemeinen schneller" sind oder dass sie nur für "sehr kurze Aufgaben" nützlich sind (im Idealfall sollte kein Synchronisationsobjekt länger als unbedingt erforderlich gehalten werden, je).
Der einzige wichtige Unterschied besteht darin, wie sich die verschiedenen Ansätze bei einer Überlastung verhalten .

Ein gut entworfenes System weist normalerweise eine geringe oder keine Überlastung auf (dies bedeutet, dass nicht alle Threads versuchen, die Sperre genau zur gleichen Zeit zu erfassen). Beispielsweise würde man normalerweise keinen Code schreiben, der eine Sperre erwirbt, dann ein halbes Megabyte zip-komprimierter Daten aus dem Netzwerk lädt, dekodiert und analysiert Daten und ändert schließlich eine gemeinsam genutzte Referenz (Anhängen von Daten an einen Container usw.), bevor die Sperre aufgehoben wird. Stattdessen würde man die Sperre nur zum Zweck des Zugriffs auf die gemeinsam genutzte Ressource erwerben.
Da dies bedeutet, dass es außerhalb des kritischen Abschnitts erheblich mehr Arbeit gibt als innerhalb des kritischen Abschnitts, ist die Wahrscheinlichkeit, dass sich ein Thread innerhalb des kritischen Abschnitts befindet, natürlich relativ gering, und daher kämpfen nur wenige Threads gleichzeitig um die Sperre . Natürlich versuchen ab und zu zwei Threads gleichzeitig, die Sperre zu erhalten (wenn dies nicht passieren könnte , bräuchten Sie keine Sperre !), aber dies ist eher die Ausnahme als die Regel in einem "gesunden" System.

In einem solchen Fall übertrifft ein Spinlock ein Semaphor erheblich , da der Aufwand für das Erfassen des Spinlocks im Vergleich dazu nur ein Dutzend Zyklen beträgt, wenn es keine Sperrenüberlastung gibt zu Hunderten/Tausenden von Zyklen für einen Kontextwechsel oder 10-20 Millionen Zyklen für den Verlust des Restes einer Zeitscheibe.

Auf der anderen Seite wird ein Spinlock bei hoher Überlastung oder wenn die Sperre über einen längeren Zeitraum gehalten wird (manchmal kann man es einfach nicht ändern!) Wahnsinnig viele CPU-Zyklen verbrennen, um nichts zu erreichen.
Ein Semaphor (oder Mutex) ist in diesem Fall eine viel bessere Wahl, da es einem anderen Thread ermöglicht, währenddessen nützliche Aufgaben auszuführen Zeit. Wenn kein anderer Thread etwas Nützliches zu tun hat, kann das Betriebssystem die CPU drosseln und Wärme reduzieren/Energie sparen.

Auf einem Single-Core-System ist ein Spinlock bei einer Überlastung der Sperren ziemlich ineffizient, da ein sich drehender Thread seine gesamte Zeit damit verschwendet, auf eine Zustandsänderung zu warten, die möglicherweise nicht stattfinden kann (erst wenn der freigegebene Thread geplant ist, was passiert nicht während der wartende Thread läuft!). Aus diesem Grund dauert der Erwerb der Sperre bei beliebiger Konflikte im besten Fall etwa 1 1/2 Zeitscheiben (vorausgesetzt, der freigebende Thread ist der nächste) geplant wird), was nicht sehr gutes Verhalten ist.

4. Wie sie implementiert sind
Ein Semaphor wird heutzutage typischerweise sys_futex unter Linux (optional mit einem Spinlock, der nach wenigen Versuchen beendet wird).
Ein Spinlock wird normalerweise mithilfe atomarer Operationen implementiert, ohne dass vom Betriebssystem bereitgestellte Elemente verwendet werden. In der Vergangenheit bedeutete dies, entweder Compiler-eigene oder nicht-portable Assembler-Anweisungen zu verwenden. Mittlerweile haben sowohl C++ 11 als auch C11 atomare Operationen als Teil der Sprache. Abgesehen von der allgemeinen Schwierigkeit, nachweislich korrekten Code ohne Sperren zu schreiben, ist es jetzt möglich, Code ohne Sperren in einem vollständig portablen und (fast) schmerzlos.

119
Damon

ganz einfach, ein Semaphor ist ein "nachgebendes" Synchronisationsobjekt, ein Spinlock ist ein "busywait" -Objekt. (Semaphoren haben ein wenig mehr zu bieten, da sie mehrere Threads synchronisieren, im Gegensatz zu einem Mutex oder Guard oder Monitor oder einem kritischen Abschnitt, der einen Codebereich vor einem einzelnen Thread schützt.)

Unter bestimmten Umständen würden Sie ein Semaphor verwenden, aber verwenden Sie ein Spinlock, bei dem Sie für eine sehr kurze Zeit sperren. Das Sperren ist mit Kosten verbunden, insbesondere, wenn Sie viel sperren. In solchen Fällen kann es effizienter sein, eine kurze Zeit lang zu sperren, während darauf gewartet wird, dass die geschützte Ressource entsperrt wird. Offensichtlich gibt es einen Leistungseinbruch, wenn Sie zu lange drehen.

in der Regel sollten Sie ein Semaphor verwenden, wenn Sie länger als ein Thread-Quantum drehen.

73
gbjbaanb

Abgesehen von dem, was Yoav Aviram und gbjbaanb gesagt haben, war der andere wichtige Punkt, dass Sie auf einem Computer mit nur einer CPU niemals einen Spin-Lock verwenden würden, wohingegen ein Semaphor auf einem solchen Computer Sinn machen würde. Heutzutage ist es häufig schwierig, einen Computer ohne mehrere Kerne oder Hyperthreading oder Ähnliches zu finden. Wenn Sie jedoch nur eine einzige CPU haben, sollten Sie Semaphore verwenden. (Ich vertraue darauf, dass der Grund offensichtlich ist. Wenn die einzelne CPU damit beschäftigt ist, darauf zu warten, dass etwas anderes die Sperre aufhebt, diese jedoch auf der einzigen CPU ausgeführt wird, ist es unwahrscheinlich, dass die Sperre aufgehoben wird, bis der aktuelle Prozess oder Thread von verhindert wird das O/S, was eine Weile dauern kann und nichts Sinnvolles passiert, bis die Preemption eintritt.)

25

Von Linux-Gerätetreibern von Rubinni

Im Gegensatz zu Semaphoren können Spinlocks in Code verwendet werden, der nicht schlafen kann, z. B. Interrupt-Handler

18
Zbyszek

Ich bin kein Kernel-Experte, aber hier sind einige Punkte:

Selbst ein Einprozessor-Rechner kann Spin-Locks verwenden, wenn beim Kompilieren des Kernels die Kernel-Preemption aktiviert ist. Wenn die Kernel-Preemption deaktiviert ist, wird Spin-Lock (möglicherweise) zu einer void -Anweisung erweitert.

Wenn wir versuchen, Semaphore mit Spin-Lock zu vergleichen, bezieht sich Semaphore meiner Meinung nach auf das im Kernel verwendete - NICHT auf das für IPC (userland)).

Grundsätzlich sollte Spin-Lock verwendet werden, wenn der kritische Bereich klein ist (kleiner als der Overhead von Schlaf/Aufwachen) und der kritische Bereich nichts aufruft, was schlafen kann! Ein Semaphor wird verwendet, wenn der kritische Bereich größer ist und schlafen kann.

Raman Chalotra.

8
Raman Chalotra

Spinlock bezieht sich auf eine Implementierung der Inter-Thread-Verriegelung unter Verwendung von maschinenabhängigen Montageanweisungen (wie z. B. Test-and-Set). Dies wird als Spinlock bezeichnet, da der Thread einfach in einer Schleife wartet ("Spins") und wiederholt prüft, bis die Sperre verfügbar ist ("busy wait"). Spinlocks werden als Ersatz für Mutexe verwendet, die von Betriebssystemen (nicht von der CPU) bereitgestellt werden, da Spinlocks eine bessere Leistung erbringen, wenn sie für kurze Zeit gesperrt sind.

Eine Semaphor ist eine Einrichtung, die von Betriebssystemen für IPC bereitgestellt wird. Daher besteht ihr Hauptzweck in der Kommunikation zwischen Prozessen. Da es sich um eine vom Betriebssystem bereitgestellte Funktion handelt, ist die Leistung nicht so gut wie bei einem Spinlock für die Verriegelung zwischen den Köpfen (obwohl dies möglich ist). Semaphore eignen sich besser zum Sperren für längere Zeiträume.

Das heißt - Splinlocks in Assembly zu implementieren ist schwierig und nicht portabel.

7
yoav.aviram

Ich möchte meine Beobachtungen hinzufügen, allgemeiner und nicht sehr Linux-spezifisch.

Abhängig von der Speicherarchitektur und den Prozessorfunktionen benötigen Sie möglicherweise eine Spin-Lock-Funktion, um ein Semaphor auf einem Multi-Core- oder Multi-Prozessor-System zu implementieren, da in solchen Systemen eine Race-Bedingung auftreten kann, wenn zwei oder mehr Threads/Prozesse benötigt werden ein Semaphor erwerben.

Ja, wenn Ihre Speicherarchitektur das Sperren eines Speicherbereichs durch einen Kern/Prozessor vorsieht, der alle anderen Zugriffe verzögert, und wenn Ihr Prozessor ein Test-and-Set anbietet, können Sie ein Semaphor ohne Spin-Lock implementieren (aber sehr vorsichtig! ).

Da jedoch einfache/kostengünstige Multi-Core-Systeme entwickelt wurden (ich arbeite in eingebetteten Systemen), unterstützen nicht alle Speicherarchitekturen solche Multi-Core-/Multiprozessor-Funktionen, sondern nur Test-and-Set- oder gleichwertige. Dann könnte eine Implementierung wie folgt aussehen:

  • erreiche die Drehsperre (beschäftigt warten)
  • versuchen Sie, das Semaphor zu erwerben
  • lassen Sie die Drehsperre los
  • wenn das Semaphor nicht erfolgreich erfasst wurde, setzen Sie den aktuellen Thread aus, bis das Semaphor freigegeben wird. Fahren Sie andernfalls mit dem kritischen Abschnitt fort

Das Freigeben des Semaphors müsste wie folgt implementiert werden:

  • nimm das Drehschloss
  • lassen Sie das Semaphor los
  • lassen Sie die Drehsperre los

Ja, und für einfache binäre Semaphore auf OS-Ebene wäre es möglich, nur ein Spin-Lock als Ersatz zu verwenden. Aber nur, wenn die zu schützenden Codeabschnitte wirklich sehr klein sind.

Wie bereits erwähnt, sollten Sie beim Implementieren Ihres eigenen Betriebssystems vorsichtig sein. Das Debuggen solcher Fehler macht Spaß (meiner Meinung nach nicht von vielen geteilt), ist aber meistens sehr mühsam und schwierig.

6
Johan Bezem

Spinlock wird nur verwendet, wenn Sie ziemlich sicher sind, dass Ihr erwartetes Ergebnis sehr bald eintreten wird, bevor die Ausführungszeit Ihres Threads abläuft.

Beispiel: Im Gerätetreibermodul schreibt der Treiber "0" in das Hardware-Register R0 und muss jetzt warten, bis dieses R0-Register 1 wird. Das H/W liest das R0 und führt einige Arbeiten aus und schreibt "1" in das R0. Dies ist im Allgemeinen schnell (in Mikrosekunden). Jetzt ist Spinnen viel besser als einschlafen und von der H/W unterbrochen zu werden. Während des Spinnens muss natürlich auf einen H/W-Fehler geachtet werden!

Es gibt absolut keinen Grund für eine Benutzeranwendung, sich zu drehen. Das ergibt keinen Sinn. Sie werden sich drehen, damit ein Ereignis eintritt, und dieses Ereignis muss von einer anderen Anwendung auf Benutzerebene abgeschlossen werden, was innerhalb des schnellen Zeitrahmens niemals garantiert wird. Also, ich werde überhaupt nicht im Benutzermodus drehen. Ich möchte lieber schlafen () oder mutexlock () oder semaphore lock () im Benutzermodus.

1
CancerSoftware

Ein "Mutex" (oder "gegenseitige Ausschlusssperre") ist ein Signal, das zwei oder mehr asynchrone Prozesse verwenden können, um eine gemeinsam genutzte Ressource für die ausschließliche Verwendung zu reservieren. Der erste Prozess, der den Besitz des "Mutex" erlangt, erhält auch den Besitz der gemeinsam genutzten Ressource. Andere Prozesse müssen warten, bis der erste Prozess den Besitz des "Mutex" freigegeben hat, bevor sie versuchen, ihn abzurufen.

Das gebräuchlichste Locking-Grundelement im Kernel ist das Spinlock. Das Spinlock ist ein sehr einfaches Einzelhalterschloss. Wenn ein Prozess versucht, ein Spinlock zu erlangen, und es nicht verfügbar ist, versucht der Prozess es weiter (dreht sich), bis er das Lock erlangen kann. Diese Einfachheit schafft ein kleines und schnelles Schloss.

1
user1011455

Von was ist der Unterschied zwischen Spin-Locks und Semaphoren? von Maciej Piechotka :

Beide verwalten eine begrenzte Ressource. Ich werde zuerst den Unterschied zwischen binärem Semaphor (Mutex) und Spin Lock beschreiben.

Schleudersperren Führe eine ausgelastete Wartezeit durch - d. H.

 while (try_acquire_resource ()); 
 ...  
Veröffentlichung();

Es führt ein sehr leichtes Sperren/Entsperren durch, aber wenn der Sperr-Thread von einem anderen vorbelegt wird, der versucht, auf dieselbe Ressource zuzugreifen, versucht der zweite einfach, Ressourcen zu beschaffen, bis ihm die CPU-Quanten ausgehen.
Mutex verhalten sich dagegen eher wie:

 if (! try_lock ()) {
 add_to_waiting_queue (); 
 wait (); 
} 
 ... 
 process * p = get_next_process_from_waiting_queue (); 
 p-> wakeUp (); 

Wenn der Thread versucht, gesperrte Ressourcen zu erhalten, wird er ausgesetzt, bis er für ihn verfügbar ist. Das Sperren/Entsperren ist viel schwerer, aber das Warten ist "kostenlos" und "fair".

Semaphore ist eine Sperre, die mehrfach (aus der Initialisierung bekannt) verwendet werden darf - zum Beispiel dürfen 3 Threads gleichzeitig die Ressource halten, aber nicht mehr. Es wird zum Beispiel in Erzeuger-/Verbraucherproblemen oder allgemein in Warteschlangen verwendet:

 P (resources_sem) 
 Resource = resources.pop () 
 ... 
 Resources.Push (resources) 
 V (resources_sem) 

nterschied zwischen Semaphor, Mutex & Spinlock?

Sperren unter Linux

1
vinayak jadi

spin Lock kann nur von einem Prozess gehalten werden, während Semaphor von einem oder mehreren Prozessen gehalten werden kann. Drehen Sie die Sperre, und warten Sie, bis der Prozess eine Sperre aufhebt und dann eine Sperre erwirbt. Semaphor ist Schlafsperre, dh er wartet und geht schlafen.

0
sanket31