Kritische Abschnittsobjekte

Ein kritisches Abschnittsobjekt bietet eine Synchronisierung, die der von einem Mutex-Objekt bereitgestellt wird, mit dem Unterschied, dass ein kritischer Abschnitt nur von den Threads eines einzelnen Prozesses verwendet werden kann. Kritische Abschnittsobjekte können nicht prozessübergreifend freigegeben werden.

Ereignis-, Mutex- und Semaphorobjekte können auch in einer Einzelprozessanwendung verwendet werden, aber kritische Abschnittsobjekte bieten einen etwas schnelleren und effizienteren Mechanismus für die Synchronisierung mit gegenseitigem Ausschluss (eine prozessorspezifische Test- und Setanweisung). Wie ein Mutex-Objekt kann ein kritisches Abschnittsobjekt jeweils nur einem Thread gehören, was es nützlich macht, um eine freigegebene Ressource vor gleichzeitigem Zugriff zu schützen. Im Gegensatz zu einem Mutex-Objekt kann nicht festgestellt werden, ob ein kritischer Abschnitt aufgegeben wurde.

Ab Windows Server 2003 mit Service Pack 1 (SP1) erhalten Threads, die auf einen kritischen Abschnitt warten, den kritischen Abschnitt nicht auf einer First-Come-First-Serve-Basis. Diese Änderung erhöht die Leistung für den meisten Code erheblich. Einige Anwendungen hängen jedoch von der FIFO-Reihenfolge (First-In, First-Out) ab und funktionieren unter aktuellen Windows-Versionen möglicherweise schlecht oder gar nicht (z. B. Anwendungen, die kritische Abschnitte als Ratenbegrenzung verwendet haben). Um sicherzustellen, dass Ihr Code weiterhin ordnungsgemäß funktioniert, müssen Sie möglicherweise eine zusätzliche Synchronisierungsebene hinzufügen. Angenommen, Sie verfügen über einen Producerthread und einen Consumerthread, die ein kritisches Abschnittsobjekt verwenden, um ihre Arbeit zu synchronisieren. Erstellen Sie zwei Ereignisobjekte, eines für jeden Thread, der verwendet werden soll, um zu signalisieren, dass er bereit ist, damit der andere Thread fortfahren kann. Der Consumerthread wartet, bis der Producer sein Ereignis signalisiert, bevor er in den kritischen Abschnitt eintritt, und der Producerthread wartet, bis der Consumerthread sein Ereignis signalisiert, bevor er in den kritischen Abschnitt eintritt. Nachdem jeder Thread den kritischen Abschnitt verlassen hat, signalisiert er seinem Ereignis, dass der andere Thread freigegeben wird.

Windows Server 2003 und Windows XP: Threads, die auf einen kritischen Abschnitt warten, werden einer Warteschleife hinzugefügt. sie werden geweckt und erhalten den kritischen Abschnitt in der Regel in der Reihenfolge, in der sie der Warteschlange hinzugefügt wurden. Wenn dieser Warteschlange threads jedoch schnell genug hinzugefügt werden, kann die Leistung aufgrund der Zeit, die benötigt wird, um jeden wartenden Thread zu wecken, beeinträchtigt werden.

Der Prozess ist für die Zuweisung des von einem kritischen Abschnitt verwendeten Arbeitsspeichers verantwortlich. Dies geschieht in der Regel durch einfaches Deklarieren einer Variablen vom Typ CRITICAL_SECTION. Bevor die Threads des Prozesses ihn verwenden können, initialisieren Sie den kritischen Abschnitt mithilfe der Funktion InitializeCriticalSection oder InitializeCriticalSectionAndSpinCount .

Ein Thread verwendet die Funktion EnterCriticalSection oder TryEnterCriticalSection , um den Besitz eines kritischen Abschnitts anzufordern. Es verwendet die LeaveCriticalSection-Funktion , um den Besitz eines kritischen Abschnitts freizugeben. Wenn sich das Objekt des kritischen Abschnitts derzeit im Besitz eines anderen Threads befindet, wartet EnterCriticalSection unbegrenzt auf den Besitz. Wenn dagegen ein Mutex-Objekt zum gegenseitigen Ausschluss verwendet wird, akzeptieren die Wartefunktionen ein angegebenes Timeoutintervall. Die TryEnterCriticalSection-Funktion versucht, in einen kritischen Abschnitt zu gelangen, ohne den aufrufenden Thread zu blockieren.

Wenn ein Thread einen kritischen Abschnitt besitzt, kann er zusätzliche Aufrufe von EnterCriticalSection oder TryEnterCriticalSection ausführen, ohne die Ausführung zu blockieren. Dadurch wird verhindert, dass ein Thread selbst deadlockt, während er auf einen kritischen Abschnitt wartet, den er bereits besitzt. Um seinen Besitz freizugeben, muss der Thread LeaveCriticalSection für jedes Mal aufrufen, wenn er in den kritischen Abschnitt gelangt ist. Es gibt keine Garantie für die Reihenfolge, in der wartende Threads den Besitz des kritischen Abschnitts erhalten.

Ein Thread verwendet die Funktion InitializeCriticalSectionAndSpinCount oder SetCriticalSectionSpinCount , um eine Drehanzahl für das Kritische Abschnittsobjekt anzugeben. Drehen bedeutet, dass der Thread, wenn ein Thread versucht, einen kritischen Abschnitt zu erhalten, der gesperrt ist, in eine Schleife wechselt, überprüft, ob die Sperre aufgehoben wird, und wenn die Sperre nicht freigegeben wird, wechselt der Thread in den Ruhezustand. Auf Einzelprozessorsystemen wird die Anzahl der Drehvorgänge ignoriert, und die Anzahl der kritischen Abschnitte wird auf 0 (null) festgelegt. Wenn auf Mehrprozessorsystemen der kritische Abschnitt nicht verfügbar ist, dreht der aufrufende Thread dwSpinCount mal, bevor er einen Wartevorgang für einen Semaphor ausführt, der dem kritischen Abschnitt zugeordnet ist. Wenn der kritische Abschnitt während dem Spinvorgang frei wird, vermeidet der aufrufende Thread den Wartevorgang.

Jeder Thread des Prozesses kann die DeleteCriticalSection-Funktion verwenden, um die Systemressourcen freizugeben, die beim Initialisieren des kritischen Abschnittsobjekts zugewiesen werden. Nachdem diese Funktion aufgerufen wurde, kann das kritische Abschnittsobjekt nicht mehr für die Synchronisierung verwendet werden.

Wenn ein kritisches Abschnittsobjekt gehört, sind nur die Threads betroffen, die in einem Aufruf von EnterCriticalSection auf den Besitz warten. Threads, die nicht warten, können weiterhin ausgeführt werden.

Mutex-Objekte

Verwenden von Kritischen Abschnittsobjekten