Sdílet prostřednictvím


Zadávání poznámek o chování při zamykání

Abyste se vyhnuli chybám souběžnosti ve vícevláknových aplikacích, vždy postupujte podle odpovídající disciplíny uzamčení a používejte poznámky SAL.

Chyby souběžnosti se obtížně reprodukují, diagnostikují a ladí, protože nejsou deterministické. Při navrhování těla kódu s více vlákny, které má více než několik vláken, je v nejlepším případě obtížné prokládání vláken a stává se nepraktické. Proto je vhodné dodržovat disciplínu uzamykání ve vícevláknových programech. Například dodržování pořadí uzamčení při získávání více zámků pomáhá vyhnout se zablokování a získání správného zámku ochrany před přístupem ke sdílenému prostředku pomáhá zabránit podmínkám časování.

Zdánlivě jednoduchá pravidla uzamčení bohužel může být překvapivě obtížné dodržovat v praxi. Základním omezením v dnešních programovacích jazycích a kompilátorech je, že přímo nepodporují specifikaci a analýzu požadavků na souběžnost. Programátoři se musí spoléhat na neformální komentáře ke kódu, aby vyjádřili své záměry o tom, jak používají zámky.

Poznámky SAL souběžnosti jsou navržené tak, aby vám pomohly určit vedlejší účinky uzamčení, odpovědnost zamykání, opatrovnictví dat, hierarchii pořadí uzamčení a další očekávané chování uzamčení. Díky explicitnímu nastavení implicitních pravidel poskytují poznámky ke souběžnosti SAL konzistentní způsob, jak dokumentovat, jak váš kód používá pravidla uzamčení. Poznámky souběžnosti také zlepšují schopnost nástrojů pro analýzu kódu najít podmínky časování, zablokování, neshodované synchronizační operace a další drobné chyby souběžnosti.

Obecné pokyny

Pomocí poznámek můžete uvést kontrakty odvozené z definic funkcí mezi implementacemi (volané) a klienty (volajícími). Můžete také vyjádřit invarianty a další vlastnosti programu, které mohou dále zlepšit analýzu.

SAL podporuje mnoho různých druhů zamykání primitiv – například kritické oddíly, mutexy, zámky číselníku a další objekty prostředků. Mnoho poznámek souběžnosti bere výraz zámku jako parametr. Podle konvence je zámek označen výrazem cesty podkladového objektu lock.

Některá pravidla vlastnictví vlákna, která je potřeba mít na paměti:

  • Zámky číselníku jsou neschůdné zámky, které mají jasné vlastnictví vlákna.

  • Mtexy a kritické oddíly jsou počítány zámky, které mají jasné vlastnictví vlákna.

  • Semafory a události jsou počítány zámky, které nemají jasné vlastnictví vlákna.

Uzamykání poznámek

Následující tabulka uvádí poznámky k uzamčení.

Poznámka Popis
_Acquires_exclusive_lock_(expr) Anotuje funkci a označuje, že ve stavu post se funkce zvýší o jeden exkluzivní počet zámků objektu zámku, který má název expr.
_Acquires_lock_(expr) Anotuje funkci a označuje, že ve stavu post se funkce zvýší o jeden počet zámků objektu zámku, který má název expr.
_Acquires_nonreentrant_lock_(expr) Zámek, který má název expr , se získá. Pokud je zámek již uložený, zobrazí se chyba.
_Acquires_shared_lock_(expr) Anotuje funkci a označuje, že ve stavu post se funkce zvýší o jeden sdílený počet zámků objektu zámku, který má název expr.
_Create_lock_level_(name) Příkaz, který deklaruje symbol name jako úroveň zámku, aby jej bylo možné použít v poznámkách _Has_Lock_level_ a _Lock_level_order_.
_Has_lock_kind_(kind) Anotuje libovolný objekt, aby se upřesní informace o typu objektu prostředku. Někdy se běžný typ používá pro různé druhy prostředků a přetížený typ nestačí k rozlišení sémantických požadavků mezi různými prostředky. Tady je seznam předdefinovaných kind parametrů:

_Lock_kind_mutex_
Zamknout ID typu pro mutexy.

_Lock_kind_event_
Zamknout ID typu pro události

_Lock_kind_semaphore_
Zamknout ID typu pro semaphores.

_Lock_kind_spin_lock_
ID typu zámek pro zámek číselníku

_Lock_kind_critical_section_
Zamknout ID typu pro důležité oddíly
_Has_lock_level_(name) Anotuje objekt zámku a dává mu úroveň namezámku .
_Lock_level_order_(name1, name2) Příkaz, který dává pořadí uzamčení mezi name1 a name2. Zámky, které mají úroveň name1 , musí být získány před zámky, které mají úroveň name2.
_Post_same_lock_(expr1, expr2) Anotuje funkci a označuje, že ve stavu post jsou tyto dva zámky expr1 , a expr2, jsou považovány za stejné objekty zámku.
_Releases_exclusive_lock_(expr) Anotuje funkci a označuje, že ve stavu post se funkce dekrementuje o jeden exkluzivní počet zámků objektu zámku, který má název expr.
_Releases_lock_(expr) Anotuje funkci a označuje, že ve stavu post se funkce dekrementuje o jeden počet zámků objektu zámku, který má název expr.
_Releases_nonreentrant_lock_(expr) Zámek, který má název expr , se uvolní. Pokud se zámek aktuálně neuchová, zobrazí se chyba.
_Releases_shared_lock_(expr) Anotuje funkci a označuje, že ve stavu post se funkce sníží o jeden sdílený počet zámků objektu zámku, který má název expr.
_Requires_lock_held_(expr) Anotuje funkci a označuje, že v předběžném stavu je počet zámků objektu, který má název expr , alespoň jeden.
_Requires_lock_not_held_(expr) Anotuje funkci a označuje, že v předběžném stavu počet zámků objektu pojmenovaného hodnotou expr je nula.
_Requires_no_locks_held_ Anotuje funkci a označuje, že počet zámků všech zámků, které jsou známé kontrolním uzlu, jsou nula.
_Requires_shared_lock_held_(expr) Anotuje funkci a označuje, že v předběžném stavu je počet sdílených zámků objektu pojmenovaného alespoň jedním objektem expr .
_Requires_exclusive_lock_held_(expr) Anotuje funkci a označuje, že v předběžném stavu je výhradní počet zámků objektu pojmenovaného alespoň jedním objektem expr .

Vnitřní objekty SAL pro nevyexponované objekty uzamčení

Některé objekty zámků nejsou vystaveny implementací přidružených zamykacích funkcí. Následující tabulka uvádí vnitřní proměnné SAL, které umožňují poznámky u funkcí, které pracují s těmito nevyexponovanými uzamčenými objekty.

Poznámka Popis
_Global_cancel_spin_lock_ Popisuje zámek zrušení otáčení.
_Global_critical_region_ Popisuje kritickou oblast.
_Global_interlock_ Popisuje vzájemně propojené operace.
_Global_priority_region_ Popisuje oblast priority.

Poznámky sdíleného přístupu k datům

Následující tabulka uvádí poznámky pro přístup ke sdíleným datům.

Poznámka Popis
_Guarded_by_(expr) Anotuje proměnnou a označuje, že při každém přístupu k proměnné je počet zámků objektu zámku pojmenovaného alespoň jedním objektem expr .
_Interlocked_ Anotuje proměnnou a je ekvivalentní ._Guarded_by_(_Global_interlock_)
_Interlocked_operand_ Parametr funkce s poznámkami je cílový operand jedné z různých vzájemně uzamčených funkcí. Tyto operandy musí mít specifické další vlastnosti.
_Write_guarded_by_(expr) Anotuje proměnnou a označuje, že při každé změně proměnné je počet zámků objektu zámku pojmenovaného alespoň jedním objektem expr .

Inteligentní zámek a poznámky RAII

Inteligentní zámky obvykle zabalují nativní zámky a spravují jejich životnost. Následující tabulka uvádí poznámky, které lze použít s inteligentními zámky a vzory kódování RAII s podporou move sémantiky.

Poznámka Popis
_Analysis_assume_smart_lock_acquired_(lock) Informuje analyzátor, aby předpokládal, že byl získán inteligentní zámek. Tato poznámka očekává typ zámku odkazu jako jeho parametr.
_Analysis_assume_smart_lock_released_(lock) Řekne analyzátoru, aby předpokládal, že byl vydán inteligentní zámek. Tato poznámka očekává typ zámku odkazu jako jeho parametr.
_Moves_lock_(target, source) Popisuje move constructor operaci, která přenáší stav zámku z objektu source do objektu target. Považuje se target za nově vytvořený objekt, takže jakýkoli stav, který měl před ztrátou a nahrazen stavem source . Resetuje source se také do čistého stavu bez počtu zámků nebo cíle aliasů, ale aliasy ukazující na něj zůstanou beze změny.
_Replaces_lock_(target, source) Popisuje sémantiku move assignment operator , ve které se před přenosem stavu ze zdroje uvolní cílový zámek. Lze jej považovat za kombinaci předcházejícího _Moves_lock_(target, source) znaku _Releases_lock_(target).
_Swaps_locks_(left, right) Popisuje standardní swap chování, které předpokládá, že objekty left a right vyměňují jejich stav. Stav výměny zahrnuje počet zámků a cíl aliasů, pokud je k dispozici. Aliasy, které odkazují na objekty left , right zůstávají beze změny.
_Detaches_lock_(detached, lock) Popisuje scénář, ve kterém typ obálky zámku umožňuje přidružení k obsaženému prostředku. Podobá se tomu, jak std::unique_ptr funguje s interním ukazatelem: umožňuje programátorům extrahovat ukazatel a ponechat kontejner inteligentního ukazatele v čistém stavu. Podobná logika je podporována std::unique_lock a lze ji implementovat ve vlastních obálkách zámků. Odpojený zámek si zachová svůj stav (počet zámků a cíl aliasů, pokud existuje), zatímco obálka se resetuje tak, aby obsahovala nulový počet zámků a žádný cíl aliasů a přitom si zachovají vlastní aliasy. U počtu zámků (uvolnění a získání) neexistuje žádná operace. Tato poznámka se chová přesně tak, jako _Moves_lock_ kdyby byl odpojený argument return spíše než this.

Viz také