71

Wann sollte man einen Semaphor verwenden und wann sollte man eine Bedingungsvariable (CondVar) verwenden?Bedingte Variable vs Semaphor

+0

Weitere Informationen finden Sie auch im Link http://stackoverflow.com/questions/4039899/when-should-we-use-mutex-and-when-should-we-use-semaphore –

Antwort

145

Schlösser werden zum gegenseitigen Ausschluss verwendet. Wenn Sie sicherstellen möchten, dass ein Code unteilbar ist, legen Sie eine Sperre um ihn herum. Sie könnten theoretisch einen binären Semaphor verwenden, aber das ist ein Sonderfall.

Semaphore und Bedingungsvariablen, die auf dem gegenseitigen Ausschluss basieren, werden von Sperren bereitgestellt und zum Bereitstellen eines synchronisierten Zugriffs auf freigegebene Ressourcen verwendet. Sie können für ähnliche Zwecke verwendet werden.

Eine Bedingungsvariable wird im Allgemeinen verwendet, um zu verhindern, dass während der Wartezeit auf eine verfügbare Ressource Wartezeiten auftreten (wiederholtes Wiederholen während der Überprüfung einer Bedingung). Zum Beispiel, wenn Sie einen Thread (oder mehrere Threads), das nicht weiter fortgesetzt werden kann, bis eine Warteschlange leer ist, würde der busy waiting Ansatz nur etwas zu tun, wie:

//pseudocode 
while(!queue.empty()) 
{ 
    sleep(1); 
} 

Das Problem dabei ist, dass Sie verschwenden Prozessorzeit, indem Sie diesen Thread wiederholt überprüfen. Warum nicht stattdessen eine Synchronisationsvariable, die signalisiert werden kann, um dem Thread mitzuteilen, dass die Ressource verfügbar ist?

Vermutlich haben Sie irgendwo einen Thread, der Dinge aus der Warteschlange zieht. Wenn die Warteschlange leer ist, kann sie syncVar.signal() anrufen, um einen zufälligen Thread aufzuwecken, der unter syncVar.wait() eingeschlafen ist (oder es gibt normalerweise auch eine oder broadcast() Methode, um alle wartenden Threads zu aktivieren).

Normalerweise verwende ich solche Synchronisationsvariablen, wenn ein oder mehrere Threads auf eine bestimmte Bedingung warten (z. B. wenn die Warteschlange leer ist).

Semaphore können ähnlich verwendet werden, aber ich denke, dass sie besser verwendet werden, wenn Sie eine freigegebene Ressource haben, die basierend auf einer ganzen Anzahl von verfügbaren Dingen verfügbar und nicht verfügbar ist. Semaphore sind gut für Produzenten-/Konsumentensituationen, in denen Produzenten Ressourcen zuweisen und Konsumenten diese konsumieren.

Denken Sie darüber nach, wenn Sie einen Getränkeautomaten hatten. Es gibt nur eine Limo-Maschine und es ist eine gemeinsame Ressource. Sie haben einen Thread, der ein Lieferant (Produzent) ist, der dafür verantwortlich ist, die Maschine vorrätig zu halten, und N Threads, die Käufer (Verbraucher) sind, die Limonaden aus der Maschine holen wollen. Die Anzahl der Limonaden in der Maschine ist der ganzzahlige Wert, der unsere Semaphore antreibt.

Jeder Käufer (Verbraucher) Thread, der zu der Limonade Maschine kommt ruft die Semaphor down() Methode, um eine Soda zu nehmen. Dies wird eine Limonade aus der Maschine greifen und die Anzahl der verfügbaren Limonaden um 1 verringern. Wenn Limonaden verfügbar sind, wird der Code einfach weiter laufen, ohne die down()-Anweisung. Wenn keine Erfrischungsgetränke verfügbar sind, wird der Thread hier schlafen und darauf warten, benachrichtigt zu werden, wenn Soda wieder verfügbar gemacht wird (wenn sich mehr Sodas in der Maschine befinden).

Der Hersteller (Hersteller) Thread würde im Wesentlichen darauf warten, dass die Limonadenmaschine leer ist. Der Verkäufer wird benachrichtigt, wenn die letzte Limonade von der Maschine genommen wird (und ein oder mehrere Verbraucher warten möglicherweise darauf, Limonaden auszugeben). Der Verkäufer würde die Sodamaschine mit dem Semaphor up()-Verfahren auffüllen, die verfügbare Anzahl von Limonaden würde jedes Mal inkrementiert und dadurch würden die wartenden Konsumenten-Threads benachrichtigt werden, dass mehr Limonade verfügbar ist.

Die Methoden wait() und signal() einer Synchronisationsvariablen sind normalerweise in den Operationen down() und up() des Semaphors versteckt.

Sicherlich gibt es Überschneidungen zwischen den beiden Möglichkeiten. Es gibt viele Szenarien, in denen ein Semaphor oder eine Bedingungsvariable (oder eine Reihe von Bedingungsvariablen) Ihren Zwecken dienen kann. Beide Semaphore und Bedingungsvariablen sind einem Sperrobjekt zugeordnet, das sie zum Beibehalten des gegenseitigen Ausschlusses verwenden, aber dann bieten sie zusätzliche Funktionen oberhalb der Sperre zum Synchronisieren der Threadausführung. Es liegt meistens an Ihnen, herauszufinden, welcher für Ihre Situation am sinnvollsten ist.

Das ist nicht unbedingt die technischste Beschreibung, aber so macht es Sinn in meinem Kopf.

+6

Wow !! Ausgezeichnete Antwort. – user373215

+0

Das war sehr hilfreich. Danke Brent. – ashu

+7

Große Antwort, ich möchte hinzufügen, aus anderen Antworten so: Semaphore wird verwendet, um die Anzahl der ausführenden Threads zu steuern. Es wird eine feste Menge von Ressourcen geben. Die Ressourcenanzahl wird jedes Mal dekrementiert, wenn ein Thread das gleiche besitzt. Wenn die Semaphorzählung 0 erreicht, dürfen keine anderen Threads die Ressource erfassen. Die Threads werden blockiert, bis andere Threads Ressourcenfreigaben besitzen. Kurz gesagt, der Hauptunterschied besteht darin, wie viele Threads die Ressource auf einmal erfassen dürfen? Mutex --its EINS. Semaphore - seine DEFINED_COUNT, (so viele wie Semaphore zählen) – berkay

13

Semaphore können verwendet werden, um den exklusiven Zugriff auf Variablen zu implementieren, sie sollen jedoch für die Synchronisation verwendet werden. Mutexe hingegen haben eine Semantik, die eng mit dem gegenseitigen Ausschluss verbunden ist: Nur der Prozess, der die Ressource gesperrt hat, darf sie entsperren.

Leider können Sie die Synchronisation mit Mutexen nicht implementieren, deshalb haben wir Zustandsvariablen. Beachten Sie außerdem, dass Sie mit Bedingungsvariablen alle wartenden Threads im selben Augenblick entsperren können, indem Sie die Broadcast-Entsperrung verwenden. Dies kann nicht mit Semaphoren gemacht werden.

1

Ich Datei Zustandsvariablen unter Monitor-Synchronisation. Ich habe Semaphore und Monitore im Allgemeinen als zwei verschiedene Synchronisierungsstile gesehen. Es gibt Unterschiede zwischen den beiden in Bezug darauf, wie viele Zustandsdaten inhärent beibehalten werden und wie Sie Code modellieren wollen - aber es gibt wirklich kein Problem, das von einem, sondern von dem anderen gelöst werden kann.

Ich neige dazu, Code in Richtung Monitor-Form; In den meisten Sprachen, in denen ich arbeite, kommt es auf Mutexe, Bedingungsvariablen und einige Hintergrundzustandsvariablen an. Aber Semaphore würden das auch tun.

+2

Dies wäre eine bessere Antwort, wenn Sie erklärt, was "Monitor-Formular" ist. –

21

Lassen Sie uns enthüllen, was unter der Haube ist.

Conditional Variable ist im Wesentlichen eine Wartezeit-Warteschlange, die Operationen blockierungs warten und Wakeup-unterstützt, dh man einen Thread in die Warte-Warteschlange setzen kann, und legte den Zustand zu blockieren, und einen Faden aus von ihm erhalten, und Setzen Sie den Status auf BEREIT.

beachten, dass eine Bedingungsvariable, zwei weitere Elemente zu verwenden, nötig sind, um:

  • eine Bedingung (typischerweise implementiert, indem ein Flag oder einen Zähler-Kontrolle)
  • einen Mutex, dass die Bedingung
schützt

Das Protokoll wird dann

  1. acquire Mutex
  2. Prüfbedingung
  3. Block und Release Mutex, wenn die Bedingung wahr Mutex, sonst Mitteilung ist

Semaphore im wesentlichen ein Zähler + a + a mutex-Warteschlange. Und es kann wie es ist ohne externe Abhängigkeiten verwendet werden. Sie können es entweder als Mutex oder als bedingte Variable verwenden.

Daher Semaphor kann als eine kompliziertere Struktur als Bedingungsvariable behandelt werden, während die letzteren leichter und flexibel ist.

+0

Great write-up über die Natur dieser Primitiven! –

3

Semaphore und Zustandsgrößen sind sehr ähnlich und sind vor allem für die gleichen Zwecke verwendet. Es gibt jedoch geringfügige Unterschiede, die einen bevorzugen könnten. Um beispielsweise die Barrieresynchronisation zu implementieren, können Sie kein Semaphor verwenden. Eine Zustandsvariable ist jedoch ideal.

Barrier-Synchronisation ist, wenn Sie alle Ihre Threads warten wollen, bis alle an einem bestimmten Teil in der Thread-Funktion angekommen ist. Dies kann implementiert werden, indem eine statische Variable verwendet wird, die anfänglich den Wert der gesamten Threads aufweist, die von jedem Thread dekrementiert werden, wenn er diese Barriere erreicht. das würde bedeuten, dass wir wollen, dass jeder Thread schläft, bis der letzte ankommt. Ein Semaphor würde genau das Gegenteil tun! Mit einem Semaphor würde jeder Thread weiterlaufen und der letzte Thread (der den Semaphorwert auf 0 setzen würde) wird in den Ruhezustand gehen.

eine Zustandsvariable auf der anderen Seite ist ideal. Wenn jeder Thread die Barriere erreicht, prüfen wir, ob unser statischer Zähler Null ist. Wenn nicht, setzen wir den Thread mit der Zustandsvariablen-Wartefunktion in den Ruhezustand. Wenn der letzte Thread an der Schranke ankommt, wird der Zählerwert auf Null dekrementiert und dieser letzte Thread wird die Zustandsvariablen-Signalfunktion aufrufen, die alle anderen Threads aufweckt!