Kritische Abschnittsobjekte

Ein kritisches Abschnittsobjekt stellt die Synchronisierung ähnlich wie bei einem Mutex-Objekt bereit, außer dass ein kritischer Abschnitt nur von den Threads eines einzelnen Prozesses verwendet werden kann. Kritische Abschnittsobjekte können nicht über Prozesse hinweg freigegeben werden.

Ereignis-, Mutex- und Semaphorobjekte können auch in einer Einzelprozessanwendung verwendet werden, kritische Abschnittsobjekte bieten jedoch einen etwas schnelleren, effizienteren Mechanismus für die Synchronisierung von gegenseitigen Ausschluss (eine prozessorspezifische Test- und Setanweisung). Wie ein Mutex-Objekt kann ein kritisches Abschnittsobjekt jeweils nur einem Thread gehören, wodurch es nützlich ist, eine freigegebene Ressource vor gleichzeitigem Zugriff zu schützen. Im Gegensatz zu einem Mutex-Objekt gibt es keine Möglichkeit, festzustellen, ob ein kritischer Abschnitt verlassen wurde.

Ab Windows Server 2003 mit Service Pack 1 (SP1) erhalten Threads, die auf einem kritischen Abschnitt warten, nicht den kritischen Abschnitt auf einer first-come-First-Serve-Basis. Diese Änderung erhöht die Leistung für die meisten Code erheblich. Einige Anwendungen hängen jedoch von der First-In-First-Out-Sortierung (First-Out) ab und können bei aktuellen Versionen von Windows schlecht oder nicht ausgeführt werden (z. B. Anwendungen, die kritische Abschnitte als Ratelimiter verwenden). 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 Produzententhread und einen Consumerthread, der ein kritisches Abschnittsobjekt verwendet, um ihre Arbeit zu synchronisieren. Erstellen Sie zwei Ereignisobjekte, eine für jeden Thread, der verwendet werden soll, um zu signalisieren, dass es für den anderen Thread bereit ist, den Vorgang fortzusetzen. Der Consumerthread wartet darauf, dass der Produzent sein Ereignis signalisiert, bevor er den kritischen Abschnitt eingibt, und der Produzententhread wartet, bis der Verbraucherthread sein Ereignis signalisiert, bevor er den kritischen Abschnitt eingibt. Nachdem jeder Thread den kritischen Abschnitt verlässt, signalisiert er sein Ereignis, um den anderen Thread freizugeben.

Windows Server 2003 und Windows XP: Threads, die auf einem kritischen Abschnitt warten, werden zu einer Warteschleife hinzugefügt. Sie werden wach und erhalten im Allgemeinen den kritischen Abschnitt in der Reihenfolge, in der sie der Warteschlange hinzugefügt wurden. Wenn dieser Warteschlange jedoch Threads schnell genug hinzugefügt werden, kann die Leistung aufgrund der Zeit beeinträchtigt werden, in der jeder wartende Thread aufwachen muss.

Der Prozess ist für die Zuweisung des Arbeitsspeichers verantwortlich, der von einem kritischen Abschnitt verwendet wird. 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 EnterCriticalSection - oder TryEnterCriticalSection-Funktion , um den Besitz eines kritischen Abschnitts anzufordern. Es verwendet die LeaveCriticalSection-Funktion , um den Besitz eines kritischen Abschnitts freizugeben. Wenn das kritische Abschnittsobjekt zurzeit einem anderen Thread gehört, wartet EnterCriticalSection unbegrenzt auf den Besitz. Wenn ein Mutex-Objekt dagegen für den gegenseitigen Ausschluss verwendet wird, akzeptieren die Wait-Funktionen ein angegebenes Timeoutintervall. Die TryEnterCriticalSection-Funktion versucht, einen kritischen Abschnitt einzugeben, ohne den aufrufenden Thread zu blockieren.

Wenn ein Thread einen kritischen Abschnitt besitzt, kann er zusätzliche Aufrufe an EnterCriticalSection oder TryEnterCriticalSection vornehmen, ohne die Ausführung zu blockieren. Dadurch wird verhindert, dass sich ein Thread selbst beim Warten auf einen kritischen Abschnitt befindet, der bereits besitzt. Um den Besitz freizugeben, muss der Thread "LeaveCriticalSection " jeweils einmal aufrufen, wenn er den kritischen Abschnitt eingegeben hat. Es gibt keine Garantie für die Reihenfolge, in der wartende Threads den Besitz des kritischen Abschnitts erwerben.

Ein Thread verwendet die Funktion InitializeCriticalSectionAndSpinCount oder SetCriticalSectionSpinCount , um eine Drehanzahl für das kritische Abschnittsobjekt anzugeben. Das Drehen bedeutet, dass, wenn ein Thread versucht, einen kritischen Abschnitt zu erwerben, der gesperrt ist, der Thread eine Schleife eingibt, überprüft, ob die Sperre losgelassen wird, und wenn die Sperre nicht losgelassen wird, wird der Thread in den Ruhezustand wechselt. Bei Einzelprozessorsystemen wird die Drehzahl ignoriert, und die Kritische Abschnittsdrehzahl wird auf 0 (Null) festgelegt. Wenn der kritische Abschnitt nicht verfügbar ist, dreht der aufrufende Thread dwSpinCount-Zeiten , bevor ein Wartezeitvorgang auf einem Semaphor ausgeführt wird, das dem kritischen Abschnitt zugeordnet ist. Wenn der kritische Abschnitt während des Drehvorgangs frei wird, wird der aufrufende Thread den Wartevorgang vermeiden.

Jeder Thread des Prozesses kann die DeleteCriticalSection-Funktion verwenden, um die Systemressourcen freizugeben, die zugewiesen werden, wenn das kritische Abschnittsobjekt initialisiert wird. Nachdem diese Funktion aufgerufen wurde, kann das kritische Abschnittsobjekt nicht für die Synchronisierung verwendet werden.

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

Mutex-Objekte

Verwenden kritischer Abschnittsobjekte