Frage Bedingte Variable vs Semaphor


Wann sollte man einen Semaphor verwenden und wann sollte man eine bedingte Variable (CondVar) benutzen?


75
2017-08-18 14:19


Ursprung


Antworten:


Sperren 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 aufbauen, werden durch Sperren bereitgestellt und dienen zum Bereitstellen eines synchronisierten Zugriffs auf gemeinsam genutzte Ressourcen. Sie können für ähnliche Zwecke verwendet werden.

Eine Bedingungsvariable wird im Allgemeinen verwendet, um ein "busy waiting" (wiederholtes Wiederholen während der Prüfung einer Bedingung) zu vermeiden, während gewartet wird, bis eine Ressource verfügbar wird. Wenn Sie zum Beispiel einen Thread (oder mehrere Threads) haben, der nicht weiterlaufen kann, bis eine Warteschlange leer ist, würde der Ansatz "Busy Waiting" lauten:

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

Das Problem dabei ist, dass Sie Prozessorzeit verschwenden, 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?

//pseudocode
syncVar.lock.acquire();

while(!queue.empty())
{
   syncVar.wait();
}

//do stuff with queue

syncVar.lock.release();

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

Ich benutze im Allgemeinen 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 haben. 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 Limonadenmaschine kommt, ruft den Semaphor auf down() Methode, ein Soda zu nehmen. Dies wird eine Limo von der Maschine greifen und die Anzahl der verfügbaren Limonaden um 1 dekrementieren. Wenn Limonaden verfügbar sind, wird der Code einfach weiterlaufen down() Aussage ohne ein Problem. 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-Thread würde im Wesentlichen darauf warten, dass die Sodamaschine 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 auffüllen up()Methode würde die verfügbare Anzahl von Limonaden jedes Mal inkrementiert werden und dadurch würden die wartenden Konsumenten-Threads benachrichtigt werden, dass mehr Limonade verfügbar ist.

Das wait() und signal() Methoden einer Synchronisationsvariablen sind in der Regel versteckt down() und up() Operationen des Semaphors.

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.


156
2017-08-18 16:35



Lass uns enthüllen, was unter der Haube ist.

Bedingte Variable ist im Wesentlichen eine Warteschlange, das Blocking-Wait- und Wakeup-Operationen unterstützt, d. h. Sie können einen Thread in die Wait-Queue stellen und seinen Zustand auf BLOCK setzen, einen Thread daraus abfragen und seinen Status auf READY setzen.

Beachten Sie, dass zwei weitere Elemente benötigt werden, um eine bedingte Variable zu verwenden:

  • eine Bedingung (typischerweise durch Überprüfung einer Flagge oder eines Zählers implementiert)
  • ein Mutex, der den Zustand schützt

Das Protokoll wird dann,

  1. Mutex erwerben
  2. Zustand prüfen
  3. Blockieren und Freigeben von Mutex, wenn die Bedingung wahr ist, andernfalls Mutex freigeben

Semaphore ist im Wesentlichen ein Zähler + ein Mutex + eine 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 kann Semaphor als eine komplexere Struktur als eine bedingte Variable behandelt werden, während letztere leichter und flexibler ist.


24
2017-11-24 20:30



Semaphore können verwendet werden, um 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.


13
2017-08-18 16:44



Semaphore und Zustandsvariablen sind sehr ähnlich und werden meistens 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.

Barrieresynchronisation ist, wenn alle Threads warten sollen, bis alle an einem bestimmten Teil der Thread-Funktion angekommen sind. 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 hingegen 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!


5
2017-12-22 03:43



Ich delete 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 tendiere dazu, zur Monitorform zu kodieren; In den meisten Sprachen, in denen ich arbeite, kommt es auf Mutexe, Bedingungsvariablen und einige Hintergrundzustandsvariablen an. Aber Semaphore würden das auch tun.


1
2017-08-18 16:44