Critical Section und Mutex sind nicht betriebssystemspezifisch, ihre Konzepte von Multithreading/Multiprocessing.
Kritischer Abschnitt Ist ein Stück Code, das zu einem bestimmten Zeitpunkt nur von selbst ausgeführt werden darf (z. B. laufen 5 Threads gleichzeitig und eine Funktion namens "critical_section_function", die ein Array aktualisiert ... Sie möchten nicht, dass alle 5 Threads die Wenn also das Programm die kritische_Abschnittsfunktion() ausführt, muss keiner der anderen Threads ihre kritische_Abschnittsfunktion ausführen.
mutex* Mutex ist eine Möglichkeit, den kritischen Abschnittscode zu implementieren (stellen Sie es sich wie ein Token vor ... der Thread muss ihn besitzen, um den kritischen_Abschnittscode auszuführen)
Für Windows sind kritische Abschnitte leichter als Mutexe.
Mutexe können von Prozessen gemeinsam genutzt werden, führen aber immer zu einem Systemaufruf an den Kernel, der etwas Overhead hat.
Kritische Abschnitte können nur innerhalb eines Prozesses verwendet werden, haben aber den Vorteil, dass sie nur im Falle von Konflikten in den Kernel-Modus wechseln - Unbestrittene Übernahmen, die der Regelfall sein sollten, sind unglaublich schnell. Im Falle eines Konflikts treten sie in den Kernel ein, um auf ein Synchronisationselement (wie ein Ereignis oder eine Semaphore) zu warten.
Ich habe eine schnelle Beispiel-App geschrieben, die die Zeit zwischen den beiden vergleicht. Auf meinem System dauert ein Mutex für 1.000.000 unbestrittene Erfassungen und Freigaben über eine Sekunde. Ein kritischer Abschnitt dauert ca. 50 ms für 1.000.000 Erfassungen.
Hier ist der Testcode, ich habe ihn ausgeführt und ähnliche Ergebnisse erhalten, wenn Mutex an erster oder zweiter Stelle steht, also sehen wir keine anderen Effekte.
HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
CRITICAL_SECTION critSec;
InitializeCriticalSection(&critSec);
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
LARGE_INTEGER start, end;
// Force code into memory, so we don't see any effects of paging.
EnterCriticalSection(&critSec);
LeaveCriticalSection(&critSec);
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
EnterCriticalSection(&critSec);
LeaveCriticalSection(&critSec);
}
QueryPerformanceCounter(&end);
int totalTimeCS = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);
// Force code into memory, so we don't see any effects of paging.
WaitForSingleObject(mutex, INFINITE);
ReleaseMutex(mutex);
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
WaitForSingleObject(mutex, INFINITE);
ReleaseMutex(mutex);
}
QueryPerformanceCounter(&end);
int totalTime = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);
printf("Mutex: %d CritSec: %d\n", totalTime, totalTimeCS);
Zusätzlich zu den anderen Antworten beziehen sich die folgenden Details auf kritische Abschnitte in Windows:
- In Ermangelung von Streitigkeiten ist der Erwerb eines kritischen Abschnitts so einfach wie ein
InterlockedCompareExchange
Betrieb - Die kritische Abschnittsstruktur bietet Platz für einen Mutex. Es ist zunächst nicht zugeordnet
- Wenn zwischen Threads ein Konflikt um einen kritischen Abschnitt besteht, wird der Mutex zugewiesen und verwendet. Die Leistung des kritischen Abschnitts wird auf die des Mutex herabgesetzt
- Wenn Sie eine hohe Konkurrenz erwarten, können Sie den kritischen Abschnitt zuweisen, indem Sie eine Spin-Anzahl angeben.
- Wenn es eine Konkurrenz um einen kritischen Abschnitt mit einer Spin-Zählung gibt, dreht sich der Thread, der versucht, den kritischen Abschnitt zu erwerben, für so viele Prozessorzyklen (busy-wait). Dies kann zu einer besseren Leistung als das Schlafen führen, da die Anzahl der Zyklen zum Durchführen eines Kontextwechsels zu einem anderen Thread viel höher sein kann als die Anzahl der Zyklen, die der besitzende Thread benötigt, um den Mutex freizugeben
- Wenn die Spin-Zählung abläuft, wird der Mutex zugewiesen
- Wenn der besitzende Thread den kritischen Abschnitt freigibt, muss überprüft werden, ob der Mutex zugewiesen ist. Wenn dies der Fall ist, wird er den Mutex so einstellen, dass er einen wartenden Thread freigibt
Ich denke, dass sie unter Linux eine "Spin-Sperre" haben, die einem ähnlichen Zweck wie der kritische Abschnitt mit einer Spin-Zählung dient.
Aus theoretischer Sicht ist ein kritischer Abschnitt ein Stück Code, das nicht von mehreren Threads gleichzeitig ausgeführt werden darf, da der Code auf gemeinsam genutzte Ressourcen zugreift.
Ein Mutex ist ein Algorithmus (und manchmal der Name einer Datenstruktur), der verwendet wird, um kritische Abschnitte zu schützen.
Semaphore und Monitore sind übliche Implementierungen eines Mutex.
In der Praxis sind viele Mutex-Implementierungen in Windows verfügbar. Sie unterscheiden sich aufgrund ihrer Implementierung hauptsächlich durch ihre Sperrebene, ihren Umfang, ihre Kosten und ihre Leistung unter verschiedenen Wettbewerbsebenen. Siehe CLR Inside Out – Using concurrency for scalability für ein Diagramm der Kosten verschiedener Mutex-Implementierungen.
Verfügbare Synchronisierungsprimitive.
- Überwachen
- Mutex
- Semaphor
- ReaderWriterLock
- ReaderWriterLockSlim
- Verriegelt
Die lock(object)
-Anweisung wird mit einem Monitor
implementiert - siehe MSDN als Referenz.
In den letzten Jahren wurde viel an nicht-blockierender Synchronisation geforscht. Ziel ist es, Algorithmen lock- oder wartefrei zu implementieren. In solchen Algorithmen hilft ein Prozess anderen Prozessen, ihre Arbeit zu beenden, damit der Prozess schließlich seine Arbeit beenden kann. Folglich kann ein Prozess seine Arbeit auch dann beenden, wenn andere Prozesse, die versucht haben, eine Arbeit auszuführen, hängen bleiben. Wenn sie Sperren verwenden, würden sie ihre Sperren nicht freigeben und verhindern, dass andere Prozesse fortgesetzt werden.