wake-up-neo.com

Bedingte Variable gegen Semaphor

Wann sollte ein Semaphor und wann eine bedingte Variable (CondVar) verwendet werden?

89
doron

Schlösser dienen zum gegenseitigen Ausschluss. Wenn Sie sicherstellen möchten, dass ein Teil des Codes atomar ist, setzen Sie eine Sperre um ihn herum. Sie könnten theoretisch ein binäres Semaphor verwenden, um dies zu tun, aber das ist ein Sonderfall.

Semaphoren und Bedingungsvariablen bauen auf dem durch Sperren bereitgestellten gegenseitigen Ausschluss auf und werden zum Bereitstellen eines synchronisierten Zugriffs auf gemeinsam genutzte Ressourcen verwendet. Sie können für ähnliche Zwecke verwendet werden.

Eine Bedingungsvariable wird im Allgemeinen verwendet, um zu vermeiden, dass zu lange gewartet wird (während eine Bedingung geprüft wird), bis eine Ressource verfügbar ist. Wenn Sie zum Beispiel einen Thread (oder mehrere Threads) haben, der erst dann fortgesetzt werden kann, wenn eine Warteschlange leer ist, besteht der Ansatz für beschäftigtes Warten darin, einfach Folgendes zu tun:

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

Das Problem dabei ist, dass Sie Prozessorzeit verschwenden, indem Sie diesen Thread wiederholt den Zustand überprüfen lassen. Warum nicht stattdessen eine Synchronisationsvariable haben, 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 syncVar.signal() aufrufen, um einen zufälligen Thread aufzuwecken, der auf syncVar.wait() schläft (oder es gibt normalerweise auch eine signalAll() oder broadcast() Methode um alle wartenden Threads aufzuwecken.

Im Allgemeinen verwende ich Synchronisationsvariablen wie diese, wenn ein oder mehrere Threads auf eine bestimmte Bedingung warten (z. B. darauf, dass die Warteschlange leer ist).

Semaphoren können auf ähnliche Weise verwendet werden, aber ich denke, dass sie besser verwendet werden, wenn Sie über eine gemeinsam genutzte Ressource verfügen, die auf der Grundlage einer ganzzahligen Anzahl verfügbarer Dinge verfügbar und nicht verfügbar sein kann. Semaphore sind gut für Produzenten-/Konsumentensituationen, in denen Produzenten Ressourcen zuweisen und Konsumenten sie verbrauchen.

Überlegen Sie, ob Sie einen Getränkeautomaten hatten. Es gibt nur einen Getränkeautomaten und es handelt sich um eine gemeinsam genutzte Ressource. Sie haben einen Thread, der ein Verkäufer (Produzent) ist, der für die Lagerhaltung der Maschine verantwortlich ist, und N Threads, die Käufer (Konsumenten) sind, die Limonaden aus der Maschine holen möchten. Die Anzahl der Limonaden in der Maschine ist der ganzzahlige Wert, der unser Semaphor steuert.

Jeder Käufer- (Verbraucher-) Thread, der zur Getränkeautomatik kommt, ruft die Semaphor-Methode down() auf, um ein Getränk zu entnehmen. Dadurch wird ein Soda aus der Maschine entnommen und die Anzahl der verfügbaren Sodas um 1 verringert. Wenn Sodas verfügbar sind, läuft der Code problemlos weiter an der Anweisung down() vorbei. Wenn keine Limonaden verfügbar sind, wird der Thread hier schlafen und darauf warten, benachrichtigt zu werden, wenn Limonaden wieder verfügbar sind (wenn sich mehr Limonaden in der Maschine befinden).

Der Hersteller-Thread würde im Wesentlichen darauf warten, dass die Soda-Maschine leer ist. Der Verkäufer wird benachrichtigt, wenn das letzte Getränk aus der Maschine entnommen wurde (und ein oder mehrere Verbraucher möglicherweise darauf warten, dass das Getränk ausgeschenkt wird). Der Verkäufer füllte die Getränkemaschine mit der Semaphor-Methode up() wieder auf, die verfügbare Anzahl von Getränken wurde jedes Mal erhöht, und dadurch wurden die wartenden Verbraucher-Threads benachrichtigt, dass mehr Getränk verfügbar ist.

Die Methoden wait() und signal() einer Synchronisationsvariablen sind in der Regel in den Operationen down() und up() des Semaphors verborgen.

Sicherlich gibt es eine Überlappung zwischen den beiden Möglichkeiten. Es gibt viele Szenarien, in denen ein Semaphor oder eine Bedingungsvariable (oder ein Satz von Bedingungsvariablen) Ihren Zwecken dienen können. Sowohl Semaphoren als auch Bedingungsvariablen sind einem Sperrobjekt zugeordnet, mit dem der gegenseitige Ausschluss aufrechterhalten wird. Anschließend bieten sie zusätzliche Funktionen über der Sperre, um die Thread-Ausführung zu synchronisieren. Meistens liegt es an Ihnen, herauszufinden, welches für Ihre Situation am sinnvollsten ist.

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

184

Lassen Sie uns verraten, was sich unter der Haube befindet.

Bedingte Variable ist im Wesentlichen eine Warteschlange, die das Blockieren von Warte- und Aufweckoperationen unterstützt, dh Sie können einen Thread in die Warteschlange stellen und seinen Status auf BLOCK setzen und einen Thread daraus entfernen und setzen Sie den Status auf BEREIT.

Beachten Sie, dass zur Verwendung einer bedingten Variablen zwei weitere Elemente erforderlich sind:

  • eine Bedingung (in der Regel durch Überprüfen eines Flags oder eines Zählers implementiert)
  • ein Mutex, der die Bedingung schützt

Das Protokoll wird dann

  1. mutex erwerben
  2. zustand prüfen
  3. blockiere und gib Mutex frei, wenn die Bedingung wahr ist, sonst gib Mutex frei

Semaphor ist im Wesentlichen ein Zähler + ein Mutex + eine Warteschlange. Und es kann so verwendet werden, wie es ist, ohne externe Abhängigkeiten. Sie können es entweder als Mutex oder als bedingte Variable verwenden.

Daher kann Semaphor als komplexere Struktur als bedingte Variable behandelt werden, während letztere leichter und flexibler ist.

38
cucufrog

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

Leider können Sie keine Synchronisation mit Mutexen implementieren, deshalb haben wir Bedingungsvariablen. Beachten Sie auch, dass Sie mit Bedingungsvariablen alle wartenden Threads gleichzeitig entsperren können, indem Sie die Broadcast-Entsperrung verwenden. Dies ist mit Semaphoren nicht möglich.

15
Dacav

semaphor- und Bedingungsvariablen sind sehr ähnlich und werden meist für die gleichen Zwecke verwendet. Es gibt jedoch geringfügige Unterschiede, die vorzuziehen sind. Um beispielsweise die Barrierensynchronisation zu implementieren, können Sie kein Semaphor verwenden. Eine Bedingungsvariable ist jedoch ideal.

Bei der Barrier-Synchronisierung sollen alle Threads warten, bis alle an einem bestimmten Teil der Thread-Funktion angekommen sind. Dies kann implementiert werden, indem eine statische Variable vorhanden ist, die anfänglich den Wert der gesamten Threads darstellt, die von jedem Thread dekrementiert werden, wenn er diese Barriere erreicht. Dies würde bedeuten, dass jeder Thread schlafen soll, bis der letzte eintrifft. Ein Semaphor würde genau das Gegenteil bewirken! Mit einem Semaphor würde jeder Thread weiterlaufen und der letzte Thread (der den Semaphor-Wert auf 0 setzt) ​​wird in den Ruhezustand versetzt.

eine Bedingungsvariable hingegen ist ideal. Wenn jeder Thread die Barriere erreicht, prüfen wir, ob unser statischer Zähler Null ist. Andernfalls wird der Thread mit der Funktion "Condition Variable Wait" in den Ruhezustand versetzt. Wenn der letzte Thread an der Barriere ankommt, wird der Zählerwert auf Null dekrementiert und dieser letzte Thread ruft die Signalfunktion der Bedingungsvariablen auf, die alle anderen Threads aufweckt!

6
Danielle

Ich archiviere Bedingungsvariablen unter Monitorsynchronisation. Ich habe allgemein Semaphoren und Monitore als zwei verschiedene Synchronisationsstile gesehen. Es gibt Unterschiede zwischen den beiden in Bezug darauf, wie viele Statusdaten inhärent aufbewahrt werden und wie Sie Code modellieren möchten - aber es gibt wirklich kein Problem, das von einem gelöst werden kann, aber nicht von dem anderen.

Ich neige dazu, in Richtung Monitorform zu codieren. In den meisten Sprachen, in denen ich arbeite, handelt es sich um Mutexe, Bedingungsvariablen und einige Hintergrundzustandsvariablen. Aber auch Semaphoren würden den Job machen.

1
Justin R