Ich habe diese Wörter im Zusammenhang mit der gleichzeitigen Programmierung gehört, aber was ist der Unterschied zwischen ihnen?
Ich habe diese Wörter im Zusammenhang mit der gleichzeitigen Programmierung gehört, aber was ist der Unterschied zwischen ihnen?
Antworten:
Durch eine Sperre kann nur ein Thread in das gesperrte Teil eintreten, und die Sperre wird nicht für andere Prozesse freigegeben.
Ein Mutex ist dasselbe wie eine Sperre, kann jedoch systemweit sein (von mehreren Prozessen gemeinsam genutzt).
Ein Semaphor macht dasselbe wie ein Mutex, erlaubt jedoch die Eingabe von x Threads. Dies kann beispielsweise verwendet werden, um die Anzahl der gleichzeitig ausgeführten CPU-, Io- oder RAM-intensiven Tasks zu begrenzen.
Einen ausführlicheren Beitrag über die Unterschiede zwischen Mutex und Semaphor finden Sie hier .
Sie haben auch Lese- / Schreibsperren, die entweder eine unbegrenzte Anzahl von Lesern oder 1 Schreiber gleichzeitig zulassen.
Es gibt viele Missverständnisse in Bezug auf diese Wörter.
Dies ist aus einem früheren Beitrag ( https://stackoverflow.com/a/24582076/3163691 ), der hier hervorragend passt:
1) Kritischer Abschnitt = Benutzerobjekt, mit dem nur ein aktiver Thread von vielen anderen innerhalb eines Prozesses ausgeführt werden kann . Die anderen nicht ausgewählten Threads (@, die dieses Objekt erfassen) werden in den Ruhezustand versetzt .
[Keine Interprozessfähigkeit, sehr primitives Objekt].
2) Mutex-Semaphor (auch bekannt als Mutex) = Kernel-Objekt, mit dem nur ein aktiver Thread von vielen anderen unter verschiedenen Prozessen ausgeführt werden kann . Die anderen nicht ausgewählten Threads (@, die dieses Objekt erfassen) werden in den Ruhezustand versetzt . Dieses Objekt unterstützt Thread-Besitz, Benachrichtigung über die Beendigung des Threads, Rekursion (mehrere Aufrufe von demselben Thread abrufen) und Vermeidung von Prioritätsumkehrungen.
[Interprozessfähigkeit, sehr sicher zu bedienen, eine Art Synchronisationsobjekt auf hoher Ebene].
3) Counting Semaphore (auch bekannt als Semaphore) = Kernel-Objekt, mit dem eine Gruppe aktiver Threads von vielen anderen ausgeführt werden kann. Die anderen nicht ausgewählten Threads (@, die dieses Objekt erfassen) werden in den Ruhezustand versetzt .
[Die Interprozessfähigkeit ist jedoch nicht sehr sicher zu verwenden, da die folgenden 'Mutex'-Attribute fehlen: Benachrichtigung über Thread-Beendigung, Rekursion?,' Vermeidung von Prioritätsumkehrungen 'usw.].
4) Und jetzt, wenn wir über 'Spinlocks' sprechen, zuerst einige Definitionen:
Kritischer Bereich = Ein Speicherbereich, der von zwei oder mehr Prozessen gemeinsam genutzt wird.
Lock = Eine Variable, deren Wert den Zugang zu einem 'kritischen Bereich' erlaubt oder verweigert. (Es könnte als einfaches 'Boolesches Flag' implementiert werden).
Besetztes Warten = Kontinuierliches Testen einer Variablen, bis ein Wert angezeigt wird.
Schließlich:
Spin-Lock (auch bekannt als Spinlock) = Eine Sperre, bei der viel gewartet wird . (Der Erwerb der Sperre erfolgt durch xchg oder ähnliche atomare Operationen ).
[Kein Thread schlafen, meistens nur auf Kernel-Ebene verwendet. Ineffizient für Code auf Benutzerebene].
Als letzten Kommentar bin ich mir nicht sicher, aber ich kann wetten, dass die oben genannten ersten 3 Synchronisierungsobjekte (Nr. 1, Nr. 2 und Nr. 3) dieses einfache Biest (Nr. 4) als Teil ihrer Implementierung verwenden.
Haben Sie einen guten Tag!.
Verweise:
-Real-Time-Konzepte für eingebettete Systeme von Qing Li mit Caroline Yao (CMP Books).
-Moderne Betriebssysteme (3.) von Andrew Tanenbaum (Pearson Education International).
-Programmieranwendungen für Microsoft Windows (4.) von Jeffrey Richter (Microsoft Programming Series).
Sie können auch einen Blick darauf werfen: https://stackoverflow.com/a/24586803/3163691
Die meisten Probleme können mit (i) nur Sperren, (ii) nur Semaphoren, ... oder (iii) einer Kombination aus beiden gelöst werden! Wie Sie vielleicht festgestellt haben, sind sie sich sehr ähnlich: Beide verhindern Rennbedingungen , beide haben acquire()
/ release()
Operationen, beide führen dazu, dass null oder mehr Threads blockiert / vermutet werden ... Der entscheidende Unterschied liegt wirklich nur darin, wie sie sperren und entsperren .
Bei beiden Sperren / Semaphoren wird beim Versuch, einen Aufruf durchzuführen, acquire()
während sich das Grundelement im Status 0 befindet, der aufrufende Thread angehalten. Für Sperren - Versuche, die Sperre im Status 1 zu erhalten, sind erfolgreich. Für Semaphore - Versuche, die Sperre in den Zuständen {1, 2, 3, ...} zu erlangen, sind erfolgreich.
Wenn für Sperren im Status 0 derselbe Thread, der zuvor aufgerufen wurde acquire()
, jetzt die Freigabe aufruft, ist die Freigabe erfolgreich. Wenn ein anderer Thread dies versucht hat, liegt es an der Implementierung / Bibliothek, was passiert (normalerweise wird der Versuch ignoriert oder ein Fehler wird ausgelöst). Bei Semaphoren im Status 0 kann jeder Thread die Freigabe aufrufen und ist erfolgreich (unabhängig davon, welcher Thread zuvor verwendet wurde, um das Semaphor in den Status 0 zu versetzen).
Aus der vorangegangenen Diskussion können wir ersehen, dass Sperren den Begriff eines Besitzers haben (der einzige Thread, der die Freigabe aufrufen kann, ist der Eigentümer), während Semaphoren keinen Eigentümer haben (jeder Thread kann die Freigabe eines Semaphors aufrufen).
Was viel Verwirrung stiftet, ist, dass es sich in der Praxis um viele Variationen dieser Definition auf hoher Ebene handelt.
Wichtige zu berücksichtigende Variationen :
acquire()
/ release()
genannt werden? - [Variiert massiv ]Dies hängt von Ihrem Buch / Dozenten / Sprache / Bibliothek / Umgebung ab.
Hier ist eine kurze Tour, in der erläutert wird, wie einige Sprachen diese Details beantworten.
pthread_mutex_t
. Standardmäßig können sie nicht mit anderen Prozessen ( PTHREAD_PROCESS_PRIVATE
) geteilt werden, Mutex haben jedoch ein Attribut namens pshared . Wenn festgelegt, wird der Mutex von Prozessen geteilt ( PTHREAD_PROCESS_SHARED
).sem_t
. Ähnlich wie bei Mutexen können Semaphoren von mehreren Prozessen gemeinsam genutzt oder für die Threads eines einzelnen Prozesses privat gehalten werden. Dies hängt vom angegebenen Argument pshared absem_init
.threading.RLock
) ist meistens dasselbe wie C / C ++ pthread_mutex_t
s. Beide sind wiedereintrittsfähig . Dies bedeutet, dass sie möglicherweise nur von demselben Thread entsperrt werden, der sie gesperrt hat. Es ist der Fall, dass sem_t
Semaphoren, threading.Semaphore
Semaphoren und theading.Lock
Sperren nicht wiedereintrittsfähig sind - denn es ist der Fall, dass jeder Thread das Sperren / Sperren des Semaphors durchführen kann.threading.Semaphore
) ist meistens dasselbe wie sem_t
. Obwohl mit sem_t
, wird eine Warteschlange mit Thread-IDs verwendet, um die Reihenfolge zu speichern, in der Threads blockiert wurden, als versucht wurde, sie zu sperren, während sie gesperrt sind. Wenn ein Thread ein Semaphor entsperrt, wird der erste Thread in der Warteschlange (falls vorhanden) als neuer Eigentümer ausgewählt. Die Thread-ID wird aus der Warteschlange entfernt und das Semaphor wird wieder gesperrt. Bei wird jedoch threading.Semaphore
ein Satz anstelle einer Warteschlange verwendet, sodass die Reihenfolge, in der Threads blockiert wurden, nicht gespeichert wird. Jeder Thread im Satz kann als nächster Eigentümer ausgewählt werden.java.util.concurrent.ReentrantLock
) ist größtenteils dasselbe wie das von C / C ++ pthread_mutex_t
und das von Python, threading.RLock
da es auch eine wiedereintrittssperre implementiert. Das Teilen von Sperren zwischen Prozessen ist in Java schwieriger, da die JVM als Vermittler fungiert. Wenn ein Thread versucht, eine Sperre zu entsperren, die er nicht besitzt, wird eine IllegalMonitorStateException
ausgelöst.java.util.concurrent.Semaphore
) ist meistens dasselbe wie sem_t
und threading.Semaphore
. Der Konstruktor für Java-Semaphoren akzeptiert einen booleschen Fairness- Parameter, der steuert, ob ein Satz (false) oder eine Warteschlange (true) zum Speichern der wartenden Threads verwendet werden soll. In der Theorie werden Semaphoren oft diskutiert, in der Praxis werden Semaphoren jedoch nicht so häufig verwendet. Ein Semaphor enthält nur den Status einer Ganzzahl, daher ist es oft ziemlich unflexibel und viele werden gleichzeitig benötigt - was zu Schwierigkeiten beim Verständnis von Code führt. Auch die Tatsache, dass jeder Thread ein Semaphor freigeben kann, ist manchmal unerwünscht. Stattdessen werden objektorientiertere / übergeordnete Synchronisationsprimitive / -abstraktionen wie "Bedingungsvariablen" und "Monitore" verwendet.
Schauen Sie sich das Multithreading-Tutorial von John Kopplin an.
Im Abschnitt Synchronisation zwischen Threads erklärt er die Unterschiede zwischen Ereignis, Sperre, Mutex, Semaphor und wartbarem Timer
Ein Mutex kann jeweils nur einem Thread gehören, sodass Threads den sich gegenseitig ausschließenden Zugriff auf eine gemeinsam genutzte Ressource koordinieren können
Kritische Abschnittsobjekte mit bieten eine ähnliche Synchronisation wie Mutex-Objekte, mit der Ausnahme, dass Objekte mit kritischen Abschnitten nur von den Threads eines einzelnen Prozesses verwendet werden können
Ein weiterer Unterschied zwischen einem Mutex und einem kritischen Abschnitt besteht darin, dass, wenn das Objekt des kritischen Abschnitts derzeit einem anderen Thread gehört,
EnterCriticalSection()
auf unbestimmte Zeit auf den Besitz gewartet wirdWaitForSingleObject()
, bei Verwendung eines Mutex ein Zeitlimit angegeben werden kannEin Semaphor behält eine Zählung zwischen Null und einem Maximalwert bei, wodurch die Anzahl der Threads begrenzt wird, die gleichzeitig auf eine gemeinsam genutzte Ressource zugreifen.
Ich werde versuchen, es mit Beispielen zu behandeln:
Sperren: Ein Beispiel, das Sie verwenden würden, lock
wäre ein freigegebenes Wörterbuch, in das Elemente (die eindeutige Schlüssel haben müssen) hinzugefügt werden.
Die Sperre würde sicherstellen, dass ein Thread nicht den Code-Mechanismus eingibt, der prüft, ob sich ein Element im Wörterbuch befindet, während ein anderer Thread (der sich im kritischen Abschnitt befindet) diese Prüfung bereits bestanden hat und das Element hinzufügt. Wenn ein anderer Thread versucht, einen gesperrten Code einzugeben, wartet er (wird blockiert), bis das Objekt freigegeben wird.
private static readonly Object obj = new Object();
lock (obj) //after object is locked no thread can come in and insert item into dictionary on a different thread right before other thread passed the check...
{
if (!sharedDict.ContainsKey(key))
{
sharedDict.Add(item);
}
}
Semaphor: Angenommen, Sie haben einen Pool von Verbindungen. Dann kann ein einzelner Thread ein Element im Pool reservieren, indem er darauf wartet, dass das Semaphor eine Verbindung erhält. Anschließend wird die Verbindung verwendet, und wenn die Arbeit erledigt ist, wird die Verbindung durch Freigeben des Semaphors freigegeben.
Das Codebeispiel, das ich liebe, ist eines der Türsteher von @Patric - hier ist es:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace TheNightclub
{
public class Program
{
public static Semaphore Bouncer { get; set; }
public static void Main(string[] args)
{
// Create the semaphore with 3 slots, where 3 are available.
Bouncer = new Semaphore(3, 3);
// Open the nightclub.
OpenNightclub();
}
public static void OpenNightclub()
{
for (int i = 1; i <= 50; i++)
{
// Let each guest enter on an own thread.
Thread thread = new Thread(new ParameterizedThreadStart(Guest));
thread.Start(i);
}
}
public static void Guest(object args)
{
// Wait to enter the nightclub (a semaphore to be released).
Console.WriteLine("Guest {0} is waiting to entering nightclub.", args);
Bouncer.WaitOne();
// Do some dancing.
Console.WriteLine("Guest {0} is doing some dancing.", args);
Thread.Sleep(500);
// Let one guest out (release one semaphore).
Console.WriteLine("Guest {0} is leaving the nightclub.", args);
Bouncer.Release(1);
}
}
}
Mutex Es wird ziemlich Semaphore(1,1)
häufig und häufig weltweit verwendet (anwendungsweit, ansonsten wohl lock
besser geeignet). Man würde global verwenden, Mutex
wenn ein Knoten aus einer global zugänglichen Liste gelöscht wird (als letztes soll ein anderer Thread etwas tun, während Sie den Knoten löschen). Wenn Sie feststellen, Mutex
ob ein anderer Thread versucht, dasselbe Mutex
zu erfassen, wird er in den Ruhezustand versetzt, bis derselbe Thread, der den Erwerb erhalten hat, ihn Mutex
freigibt.
Ein gutes Beispiel für die Erstellung eines globalen Mutex ist @deepee
class SingleGlobalInstance : IDisposable
{
public bool hasHandle = false;
Mutex mutex;
private void InitMutex()
{
string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
string mutexId = string.Format("Global\\{{{0}}}", appGuid);
mutex = new Mutex(false, mutexId);
var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
var securitySettings = new MutexSecurity();
securitySettings.AddAccessRule(allowEveryoneRule);
mutex.SetAccessControl(securitySettings);
}
public SingleGlobalInstance(int timeOut)
{
InitMutex();
try
{
if(timeOut < 0)
hasHandle = mutex.WaitOne(Timeout.Infinite, false);
else
hasHandle = mutex.WaitOne(timeOut, false);
if (hasHandle == false)
throw new TimeoutException("Timeout waiting for exclusive access on SingleInstance");
}
catch (AbandonedMutexException)
{
hasHandle = true;
}
}
public void Dispose()
{
if (mutex != null)
{
if (hasHandle)
mutex.ReleaseMutex();
mutex.Dispose();
}
}
}
dann benutze wie:
using (new SingleGlobalInstance(1000)) //1000ms timeout on global lock
{
//Only 1 of these runs at a time
GlobalNodeList.Remove(node)
}
Hoffe das spart dir etwas Zeit.
Wikipedia hat einen großartigen Abschnitt über die Unterschiede zwischen Semaphoren und Mutexen :
Ein Mutex ist im Wesentlichen dasselbe wie ein binäres Semaphor und verwendet manchmal dieselbe grundlegende Implementierung. Die Unterschiede zwischen ihnen sind:
Mutexe haben das Konzept eines Besitzers. Dies ist der Prozess, der den Mutex gesperrt hat. Nur der Prozess, der den Mutex gesperrt hat, kann ihn entsperren. Im Gegensatz dazu hat ein Semaphor kein Konzept eines Besitzers. Jeder Prozess kann ein Semaphor entsperren.
Im Gegensatz zu Semaphoren bieten Mutexe vorrangige Inversionssicherheit. Da der Mutex seinen aktuellen Besitzer kennt, ist es möglich, die Priorität des Besitzers zu erhöhen, wenn eine Aufgabe mit höherer Priorität auf den Mutex wartet.
Mutexe bieten auch Löschsicherheit, wenn der Prozess, der den Mutex enthält, nicht versehentlich gelöscht werden kann. Semaphoren bieten dies nicht.
Nach meinem Verständnis kann ein Mutex nur innerhalb eines einzelnen Prozesses verwendet werden, jedoch über mehrere Threads hinweg, während ein Semaphor über mehrere Prozesse hinweg und über die entsprechenden Thread-Sätze hinweg verwendet werden kann.
Außerdem ist ein Mutex binär (entweder gesperrt oder entsperrt), während ein Semaphor den Begriff des Zählens oder eine Warteschlange mit mehr als einer Anforderung zum Sperren und Entsperren hat.
Könnte jemand meine Erklärung überprüfen? Ich spreche im Zusammenhang mit Linux, insbesondere Red Hat Enterprise Linux (RHEL) Version 6, die Kernel 2.6.32 verwendet.
Verwenden der C-Programmierung unter einer Linux-Variante als Basis für Beispiele.
Sperren:
• Normalerweise ist eine sehr einfache Konstrukt-Binärdatei im Betrieb entweder gesperrt oder entsperrt
• Kein Konzept für Thread-Besitz, Priorität, Sequenzierung usw.
• Normalerweise eine Drehsperre, bei der der Thread kontinuierlich die Verfügbarkeit der Sperren überprüft.
• Verlässt sich normalerweise auf atomare Operationen, z. B. Testen und Setzen, Vergleichen und Tauschen, Abrufen und Hinzufügen usw.
• Erfordert normalerweise Hardware-Unterstützung für den atomaren Betrieb.
Dateisperren:
• Wird normalerweise verwendet, um den Zugriff auf eine Datei über mehrere Prozesse zu koordinieren.
• Mehrere Prozesse können die Lesesperre halten. Wenn jedoch ein einzelner Prozess die Schreibsperre hält, darf kein anderer Prozess eine Lese- oder Schreibsperre erwerben.
• Beispiel: Herde, fcntl etc ..
Mutex:
• Mutex-Funktionsaufrufe funktionieren normalerweise im Kernelraum und führen zu Systemaufrufen.
• Es verwendet das Konzept des Eigentums. Nur der Thread, der derzeit den Mutex enthält, kann ihn entsperren.
• Mutex ist nicht rekursiv (Ausnahme: PTHREAD_MUTEX_RECURSIVE).
• Wird normalerweise in Verbindung mit Bedingungsvariablen verwendet und als Argumente an z. B. pthread_cond_signal, pthread_cond_wait usw. übergeben.
• Einige UNIX-Systeme ermöglichen die Verwendung von Mutex durch mehrere Prozesse, obwohl dies möglicherweise nicht auf allen Systemen erzwungen wird.
Semaphor:
• Dies ist eine vom Kernel gepflegte Ganzzahl, deren Werte nicht unter Null fallen dürfen.
• Es kann verwendet werden, um Prozesse zu synchronisieren.
• Der Wert des Semaphors kann auf einen Wert größer als 1 festgelegt werden. In diesem Fall gibt der Wert normalerweise die Anzahl der verfügbaren Ressourcen an.
• Ein Semaphor, dessen Wert auf 1 und 0 beschränkt ist, wird als binäres Semaphor bezeichnet.
Supporting ownership
, maximum number of processes share lock
und das maximum number of allowed processes/threads in critical section
sind drei Hauptfaktoren, die den Namen / Typ des gleichzeitigen Objekts mit dem allgemeinen Namen von bestimmen lock
. Da der Wert dieser Faktoren binär ist (zwei Zustände hat), können wir sie in einer 3 * 8-Wahrheitstabelle zusammenfassen.
X Y Z Name
--- --- --- ------------------------
0 ∞ ∞ Semaphore
0 ∞ 1 Binary Semaphore
0 1 ∞ SemaphoreSlim
0 1 1 Binary SemaphoreSlim(?)
1 ∞ ∞ Recursive-Mutex(?)
1 ∞ 1 Mutex
1 1 ∞ N/A(?)
1 1 1 Lock/Monitor
Fühlen Sie sich frei, diese Tabelle zu bearbeiten oder zu erweitern. Ich habe sie als ASCII-Tabelle veröffentlicht, damit sie bearbeitet werden kann :)