Sowohl Semaphoren als auch Monitore sind Synchronisation Primitive, die in gleichzeitiger Programmierung verwendet werden, um den Zugriff auf gemeinsame Ressourcen zu verwalten und Rassenbedingungen zu verhindern. Sie unterscheiden sich jedoch in ihrer Struktur, ihrer Durchsetzung und ihrer Verwendung. Hier ist eine Aufschlüsselung der wichtigsten Unterschiede:
1. Struktur und Implementierung:
* Semaphoren:
* einfache Ganzzahlvariablen: Im Kern sind Semaphoren ganzzahlige Variablen, auf die mit Atomoperationen zugegriffen und modifiziert werden.
* Zwei atomare Operationen: Sie haben normalerweise zwei primäre Operationen:
* `wait ()` (oder `p ()` oder `Accire ()`):Verringung des Semaphorwerts. Wenn der Wert negativ wird, blockiert der Prozess/Thread, bis der Semaphorwert größer oder gleich Null ist.
* `signal ()` (oder `v ()` oder `release ()`):erhöht den Semaphorwert. Wenn auf dem Semaphor blockierte Prozesse/Threads warten, ist einer von ihnen unverschleudert.
* Keine implizite Daten Assoziation: Semaphoren binden den Synchronisationsmechanismus nicht an bestimmte Daten. Sie können ein Semaphor verwenden, um den Zugriff auf jede gemeinsame Ressource zu kontrollieren. Sie müssen jedoch den Verband manuell verwalten.
* Monitore:
* Strukturierter Ansatz: Monitore sind strukturiertere Programmierkonstrukte. Sie verkapulieren:
* Shared Data: Variablen, die die geschützte gemeinsame Ressource darstellen.
* Verfahren/Methoden: Die Vorgänge, die auf die gemeinsam genutzten Daten zugreifen und diese ändern. Dies sind die einzigen Routinen, die direkt auf die gemeinsam genutzten Daten zugreifen dürfen.
* Zustandsvariablen: (Wichtig!) Spezielle Variablen, die zum Signalen und Warten innerhalb des Monitors verwendet werden.
* explizite gegenseitige Ausschluss: Monitore bieten implizite gegenseitige Ausschluss. Nur ein Thread/Prozess kann zu einem bestimmten Zeitpunkt aktiv sein * in * im Monitor. Dieser gegenseitige Ausschluss wird vom Monitor selbst automatisch erzwungen.
2. Durchsetzung des gegenseitigen Ausschlusses:
* Semaphoren:
* Manuelle Durchsetzung: Semaphoren verlassen sich auf Programmierer, um ordentlich `wait ()` und `signal ()` -Operationen um kritische Abschnitte zu verwenden. Es liegt an dem Programmierer, sicherzustellen, dass der gegenseitige Ausschluss ordnungsgemäß beibehalten wird. Fehler können leicht eingeführt werden. Zum Beispiel kann das Vergessen eines `signal ()` zu Deadlock führen. Das Platzieren von `wait ()` oder `signal ()` außerhalb des beabsichtigten kritischen Abschnitts kann zu Parallelitätsproblemen führen.
* Fehleranfällig: Diese manuelle Durchsetzung ist anfällig für Fehler. Es ist leicht, Fehler zu machen, die gegenseitige Ausgrenzung brechen oder Deadlocks verursachen.
* Monitore:
* implizite Durchsetzung: Monitore erzwingen gegenseitige Ausschluss *implizit *. Die Sprache/das System stellt sicher, dass jeweils nur ein Thread im Monitor aktiv sein kann. Dies erleichtert es einfacher, Fehler zu behaupten und weniger anfällig für Fehler. Das Compiler- oder Laufzeitsystem behandelt die Verriegelung und Entsperrung.
3. Bedingungssynchronisation (Warten und Signalisierung):
* Semaphoren:
* Allgemeines, aber weniger strukturiert für den Zustand wartet: Semaphoren * kann * zur Zustandssynchronisation verwendet werden (z. B. Warten auf eine Ressource, die verfügbar ist). Es ist jedoch etwas umständlich. Sie benötigen normalerweise separate Semaphoren für den gegenseitigen Ausschluss und für Signalbedingungen, sodass der Code komplexer und anfällig für Fehler ist. Möglicherweise verwenden Sie Zählsemaphoren, um anzugeben, wie viele Ressourcen verfügbar sind.
* Monitore:
* Zustandsvariablen: Monitore verwenden * Zustandsvariablen * speziell für die Bedingungssynchronisation. Diese bieten Operationen wie:
* `Wait (Bedingung)`:Der aufrufende Thread veröffentlicht die Monitor -Sperre und wartet auf dem angegebenen "Zustand". Der Faden wird in eine Warteschlange platziert, die dieser Bedingungsvariablen zugeordnet ist. Das Monitor -Sperre wird freigegeben, damit andere Threads den Monitor eingeben können.
* `Signal (Bedingung)`:Ein Thread, der auf den angegebenen "Zustand" wartet, wird geweckt. Der signalisierte Gewinde ist wieder auf die Monitorverriegelung teilnehmen.
* `Broadcast (Bedingung)` (oder `signalall (Zustand)`):Awakens alle Threads, die auf den angegebenen `Bedingung warten.
* Verbesserte Struktur: Zustandsvariablen sind eng mit dem Monitor und seinen gemeinsamen Daten verbunden, was es einfacher macht, die Bedingungssynchronisation innerhalb des Monitors zu verstehen und zu begründen.
4. Besitz und Verantwortung:
* Semaphoren:
* kein klares Eigentum: Jeder Thread kann `wait ()` oder `signal ()` auf einem Semaphor. Es gibt kein Konzept, ein Semaphor zu "besitzen".
* Monitore:
* Clear Ownership: Threads müssen den Monitor eingeben (implizit die Überwachungssperrung erfassen), bevor er auf die gemeinsam genutzten Daten oder die Signalisierungsbedingungsvariablen zugreift. Dies erzwingt ein klares Eigentümermodell.
5. Wiedereintritt:
* Semaphoren:
* kein inhärentes Konzept der Wiedereinnahme: Semaphoren wissen nicht, ob der Thread, der "Wait () oder" Signal () `nennt, das Semaphor bereits enthält. Wenn Sie ein Semaphor wieder einsetzt (z. B. der gleiche Thread erwerben, kann das Semaphor mehrmals ohne Freigabe erwerben) kann leicht zu einer Deadlock führen.
* Monitore:
* im Allgemeinen nicht wiedereintran: Monitore sind normalerweise so konzipiert, dass er *nicht eingetreten ist *. Ein Thread, der das Monitor-Sperre bereits hält, kann den Monitor nicht wieder eingeben. Dies vereinfacht die Begründung über den Status des Programms, kann jedoch manchmal Umstrukturierungscode erfordern. Einige Überwachungsimplementierungen unterstützen die Wiedereinnahme, aber es ist weniger häufig.
Zusammenfassend:
| Feature | Semaphoren | Monitore |
| ------------------- | ----------------------------------------- | -------------------------------------------------- |
| Struktur | Einfache Ganzzahlvariablen | Strukturiertes Konstrukt mit Daten, Verfahren und Zustandsvariablen |
| Gegenseitiger Ausschluss | Handbuch (Programmiererverantwortung) | Implizit (durch den Monitor selbst erzwungen) |
| Zustandssynchronisation | Kann verwendet werden, aber weniger strukturiert | Explizit mit Zustandsvariablen (warten, signalisieren, übertragen) |
| Fehleranfällig | Weitere fehleranfällige aufgrund des manuellen Managements | Weniger fehleranfälliger aufgrund der implizite Durchsetzung |
| Besitz | Kein klares Eigentum | Löschen des Eigentums (Thread muss den Monitor eingeben) |
| Wiedereintritt | Im Allgemeinen nicht berücksichtigt | Im Allgemeinen nicht neu eingetragen |
Wann verwenden Sie welche:
* Semaphoren: Semaphoren werden zwar immer noch wertvoll, werden oft als primitiv auf niedrigerer Ebene angesehen. Sie sind nützlich, wenn ein einfacher Zählmechanismus erforderlich ist oder wenn die Synchronisationsanforderungen sehr einfach sind. Sie sind auch hilfreich in Systemen, bei denen Monitore nicht nativ unterstützt werden.
* Monitore: Monitore werden im Umgang mit komplexeren Synchronisationsszenarien bevorzugt. Ihr strukturierter Ansatz, ihr implizites gegenseitiges Ausschluss und die Zustandsvariablen erleichtern das Schreiben des korrekten und aufrechterhaltenen gleichzeitigen Code. Monitore eignen sich gut zum Schutz der gemeinsamen Datenstrukturen und zur Implementierung von Synchronisationsmustern. Viele moderne Programmiersprachen bieten integrierte Unterstützung für Monitore oder ähnliche Konstrukte (z. B. Javas "Synchronisierte" Schlüsselwort und "Wait ()", "notify ()", "notifyAll ()" Methoden "oder" Pythons "Threading.Lock" und "Threading.Condition.condition").
Beispiel (konzeptionell - nicht spezifische Sprache):
Semaphor -Beispiel (konzeptionell):
`` `
semaphor mutex =1; // Binäres Semaphor für gegenseitige Ausgrenzung
semaphore ressourcenceavailable =0; // Semaphor für verfügbare Ressourcen zählen
// Thread 1 (Produzent)
Warten Sie (Mutex);
// Zugriff auf die gemeinsame Ressource auf und ändern
// Ressource hinzufügen
Signal (mutex);
Signal (ressourcenversorgbar); // signalisieren, dass jetzt eine Ressource verfügbar ist
// Thread 2 (Verbraucher)
Warten Sie (ressourcenversorgbar); // Warten Sie, bis eine Ressource verfügbar ist
Warten Sie (Mutex);
// Zugriff auf die gemeinsame Ressource auf und ändern
// Ressource konsumieren
Signal (mutex);
`` `
Beispiel für Monitor (konzeptionell):
`` `
Überwachen Sie MyMonitor {
gemeinsame Daten:...;
Bedingung ressourcenversorgbar;
Methode AccessResource () {
// implizit Monitor Lock erwerben
während (Ressource ist nicht verfügbar) {
Warten Sie (ressourcenversorgbar); // Monitor Lock freigeben, warten Sie unter dem Zustand
}
// Zugriff auf gemeinsame Daten zu und ändern
// ...
Signal (ressourcenversorgbar); // Signalwartungsthread
// implizit Monitor Lock freigeben, wenn die Methode zurückgibt
}
Methode addresource () {
// implizit Monitor Lock erwerben
// Ressource zu gemeinsam genutzten Daten hinzufügen
Signal (ressourcenversorgbar); // Signalwartungsthread
// implizit Monitor Lock freigeben
}
}
`` `
Im Monitor -Beispiel wird der gegenseitige Ausschluss automatisch vom Monitor behandelt, wodurch er weniger anfällig für Fehler ist. Zustandsvariablen bieten eine strukturiertere Möglichkeit, das Warten und Signalübertragung im Vergleich zur direkten Verwendung von Semaphoren direkt für diesen Zweck zu verarbeiten.