Hinzufügen einer Anmerkung zum Sperrverhalten

Um Parallelitätsfehler in Ihrem Multithread-Programm zu vermeiden, befolgen Sie immer eine entsprechende Sperrdisziplin, und verwenden Sie SAL-Anmerkungen.

Parallelitätsfehler sind berüchtigt schwer zu reproduzieren, diagnostizieren und debuggen, da sie nicht deterministisch sind. Die Gründe für die Threadübereinschränkung sind am besten schwierig und werden unpraktisch, wenn Sie einen Codekörper entwerfen, der mehr als ein paar Threads enthält. Daher empfiehlt es sich, eine Sperrdisziplin in Ihren Multithread-Programmen zu befolgen. Wenn Sie z. B. eine Sperrreihenfolge befolgen, während Sie mehrere Sperren erwerben, vermeiden Sie Deadlocks, und das Abrufen der richtigen Schutzsperre vor dem Zugriff auf eine freigegebene Ressource hilft, Rennbedingungen zu verhindern.

Leider können scheinbar einfache Sperrregeln überraschend schwer zu befolgen sein. Eine grundlegende Einschränkung in den heutigen Programmiersprachen und Compilern besteht darin, dass sie die Spezifikation und Analyse der Parallelitätsanforderungen nicht direkt unterstützen. Programmierer müssen sich auf informelle Codekommentare verlassen, um ihre Absichten zur Verwendung von Sperren auszudrücken.

Parallelität SAL-Anmerkungen sind so konzipiert, dass Sie Sperrungs-Nebenwirkungen, Sperrverantwortung, Datenbewahrung, Hierarchie der Sperrreihenfolge und anderes erwartetes Sperrverhalten angeben können. Durch explizite explizite implizite Regeln bieten SAL-Parallelanmerkungen eine konsistente Möglichkeit, zu dokumentieren, wie Ihr Code Sperrregeln verwendet. Parallelitätsanmerkungen verbessern auch die Fähigkeit von Codeanalysetools zum Auffinden von Racebedingungen, Deadlocks, nicht übereinstimmenden Synchronisierungsvorgängen und anderen subtilen Parallelitätsfehlern.

Allgemeine Richtlinien

Mithilfe von Anmerkungen können Sie die Verträge angeben, die durch Funktionsdefinitionen zwischen Implementierungen (Angerufenen) und Clients (Aufrufer) impliziert werden. Sie können auch Invarianten und andere Eigenschaften des Programms ausdrücken, die die Analyse weiter verbessern können.

SAL unterstützt viele verschiedene Arten von Sperrgrundtypen, z. B. kritische Abschnitte, Mutexes, Drehsperren und andere Ressourcenobjekte. Viele Parallelitätsanmerkungen verwenden einen Sperrausdruck als Parameter. In der Konvention wird eine Sperre durch den Pfadausdruck des zugrunde liegenden Sperrobjekts gekennzeichnet.

Einige Threadbesitzerregeln, die Sie berücksichtigen sollten:

  • Drehsperren sind nicht gezählte Sperren, die über einen klaren Threadbesitz verfügen.

  • Mutexes und kritische Abschnitte werden gezählte Sperren, die über einen klaren Threadbesitz verfügen.

  • Semaphore und Ereignisse werden gezählte Sperren, die keinen klaren Threadbesitz haben.

Sperren von Anmerkungen

In der folgenden Tabelle sind die Sperranmerkungen aufgeführt.

Anmerkung Beschreibung
_Acquires_exclusive_lock_(expr) Kommentiert eine Funktion und gibt an, dass die Funktion im Post-Zustand um eine der exklusiven Sperranzahl des Sperrobjekts erhöht wird, von exprdem benannt wird.
_Acquires_lock_(expr) Kommentiert eine Funktion und gibt an, dass die Funktion im Post-Zustand um eine der Sperranzahl des Sperrobjekts erhöht wird, das durch exprbenannt wird.
_Acquires_nonreentrant_lock_(expr) Die von ihnen benannte expr Sperre wird erworben. Wenn die Sperre bereits gespeichert ist, wird ein Fehler gemeldet.
_Acquires_shared_lock_(expr) Kommentiert eine Funktion und gibt an, dass die Funktion im Post-Zustand um eine der freigegebenen Sperranzahl des sperrobjekts erhöht wird, das durch exprbenannt wird.
_Create_lock_level_(name) Eine Anweisung, die das Symbol name als Sperrebene deklariert, sodass es in den Anmerkungen _Has_Lock_level_ und _Lock_level_order_.
_Has_lock_kind_(kind) Kommentiert alle Objekte, um die Typinformationen eines Ressourcenobjekts zu verfeinern. Manchmal wird ein allgemeiner Typ für verschiedene Arten von Ressourcen verwendet, und der überladene Typ reicht nicht aus, um die semantischen Anforderungen zwischen verschiedenen Ressourcen zu unterscheiden. Hier ist eine Liste der vordefinierten kind Parameter:

_Lock_kind_mutex_
Sperrtyp-ID für Mutexes.

_Lock_kind_event_
Sperrtyp-ID für Ereignisse.

_Lock_kind_semaphore_
Sperrtyp-ID für Semaphore.

_Lock_kind_spin_lock_
Sperrtyp-ID für Drehsperren.

_Lock_kind_critical_section_
Sperrtyp-ID für kritische Abschnitte.
_Has_lock_level_(name) Kommentiert ein Sperrobjekt und gibt es die Sperrebene von name.
_Lock_level_order_(name1, name2) Eine Anweisung, die die Sperre reihenfolge zwischen name1 und name2. Sperren mit Ebene name1 müssen vor Sperren mit Ebene name2abgerufen werden.
_Post_same_lock_(expr1, expr2) Kommentiert eine Funktion und gibt an, dass die beiden Sperren expr1 im Post-Zustand behandelt werden und expr2wie dasselbe Sperrobjekt behandelt werden.
_Releases_exclusive_lock_(expr) Kommentiert eine Funktion und gibt an, dass die Funktion im Post-Zustand um eine der exklusiven Sperranzahl des Sperrobjekts erhöht wird, von exprdem benannt wird.
_Releases_lock_(expr) Kommentiert eine Funktion und gibt an, dass die Funktion im Post-Zustand um eine der Sperranzahl des Sperrobjekts erhöht wird, das von expr.
_Releases_nonreentrant_lock_(expr) Die von ihnen benannte expr Sperre wird freigegeben. Wenn die Sperre zurzeit nicht gehalten wird, wird ein Fehler gemeldet.
_Releases_shared_lock_(expr) Kommentiert eine Funktion und gibt an, dass die Funktion im Post-Zustand um eine der freigegebenen Sperranzahl des sperrobjekts erhöht wird, das durch exprbenannt wird.
_Requires_lock_held_(expr) Kommentiert eine Funktion und gibt an, dass im Voraus die Sperranzahl des Objekts, das benannt expr wird, mindestens eine ist.
_Requires_lock_not_held_(expr) Kommentiert eine Funktion und gibt an, dass die Sperranzahl des objekts, das durch expr "Null" benannt wird, im Vorabzustand angibt.
_Requires_no_locks_held_ Kommentiert eine Funktion und gibt an, dass die Sperranzahl aller Sperren, die der Prüfer bekannt sind, null sind.
_Requires_shared_lock_held_(expr) Kommentiert eine Funktion und gibt an, dass im Voraus die Anzahl der freigegebenen Sperre des Objekts, das benannt expr wird, mindestens eine ist.
_Requires_exclusive_lock_held_(expr) Kommentiert eine Funktion und gibt an, dass im Voraus die exklusive Sperranzahl des Objekts, das benannt expr wird, mindestens eine ist.

Systeminterne SAL-Funktionen für nicht verfügbare Sperrobjekte

Bestimmte Sperrobjekte werden nicht durch die Implementierung der zugehörigen Sperrfunktionen verfügbar gemacht. In der folgenden Tabelle sind systeminterne SAL-Variablen aufgeführt, die Anmerkungen für Funktionen ermöglichen, die auf diesen nicht exponierten Sperrobjekten arbeiten.

Anmerkung Beschreibung
_Global_cancel_spin_lock_ Beschreibt die Abbruch-Drehungssperre.
_Global_critical_region_ Beschreibt die kritische Region.
_Global_interlock_ Beschreibt verriegelte Vorgänge.
_Global_priority_region_ Beschreibt die Prioritätsregion.

Anmerkungen zum freigegebenen Datenzugriff

In der folgenden Tabelle sind die Anmerkungen für den zugriff auf freigegebene Daten aufgeführt.

Anmerkung Beschreibung
_Guarded_by_(expr) Kommentiert eine Variable und gibt an, dass bei jedem Zugriff auf die Variable die Sperranzahl des durch benannten expr Sperrobjekts mindestens eins ist.
_Interlocked_ Kommentiert eine Variable und entspricht _Guarded_by_(_Global_interlock_).
_Interlocked_operand_ Der Parameter "Kommentierte Funktion" ist der Zielopernd einer der verschiedenen Interlocked-Funktionen. Diese Operanden müssen bestimmte zusätzliche Eigenschaften aufweisen.
_Write_guarded_by_(expr) Kommentiert eine Variable und gibt an, dass bei jeder Änderung der Variablen die Sperranzahl des durch benannten expr Sperrobjekts mindestens eins ist.

Smart Lock and RAII Annotations

Intelligente Sperren umschließen in der Regel systemeigene Sperren und verwalten ihre Lebensdauer. In der folgenden Tabelle sind Anmerkungen aufgeführt, die mit intelligenten Sperren und RAII-Codierungsmustern mit Unterstützung für move Semantik verwendet werden können.

Anmerkung Beschreibung
_Analysis_assume_smart_lock_acquired_(lock) Teilt dem Analyzer mit, dass davon ausgegangen wird, dass eine intelligente Sperre erworben wurde. Diese Anmerkung erwartet einen Verweissperrtyp als Parameter.
_Analysis_assume_smart_lock_released_(lock) Teilt dem Analyzer mit, davon auszugehen, dass eine intelligente Sperre freigegeben wurde. Diese Anmerkung erwartet einen Verweissperrtyp als Parameter.
_Moves_lock_(target, source) Beschreibt einen move constructor Vorgang, der den source Sperrzustand vom Objekt in das targetObjekt überträgt. Das target Objekt wird als neu konstruiertes Objekt betrachtet, sodass jeder Zustand, den er vorher hatte, verloren geht und durch den source Zustand ersetzt wird. Das source Element wird auch auf einen sauber Zustand ohne Sperranzahl oder Aliasingziel zurückgesetzt, aber Aliase, die darauf verweisen Standard unverändert.
_Replaces_lock_(target, source) move assignment operator Beschreibt die Semantik, in der die Zielsperre freigegeben wird, bevor der Zustand von der Quelle übertragen wird. Sie können es als eine Kombination von _Moves_lock_(target, source) vorangestellten ._Releases_lock_(target)
_Swaps_locks_(left, right) Beschreibt das Standardverhalten swap , das davon ausgeht, dass Objekte left und right deren Zustand ausgetauscht werden. Der ausgetauschte Zustand umfasst die Sperranzahl und das Aliasingziel, falls vorhanden. Aliase, die auf die und right die left Objekte verweisen Standard unverändert.
_Detaches_lock_(detached, lock) Beschreibt ein Szenario, in dem ein Sperrwrappertyp die Dissoziation mit der enthaltenen Ressource zulässt. Es ähnelt der std::unique_ptr Funktionsweise des internen Zeigers: Programmierer können den Zeiger extrahieren und seinen intelligenten Zeigercontainer in einem sauber Zustand belassen. Ähnliche Logik wird unterstützt std::unique_lock und kann in benutzerdefinierten Sperrwrappern implementiert werden. Die getrennte Sperre behält ihren Zustand bei (Sperranzahl und Aliasingziel, falls vorhanden), während der Wrapper zurückgesetzt wird, um die Anzahl der Sperre und kein Aliasingziel zu enthalten, während die eigenen Aliase beibehalten werden. Es gibt keinen Vorgang bei der Sperranzahl (Freigeben und Abrufen). Diese Anmerkung verhält sich genau so, als _Moves_lock_ dass das getrennte Argument return statt .this

Siehe auch