Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Následující pravidla spolehlivosti jsou zaměřená na SQL Server; ale platí také pro libovolnou serverovou aplikaci založenou na hostiteli. Je velmi důležité, aby servery, jako je SQL Server, neunikaly prostředky a nedošlo k jejich selhání. To však nelze provést zápisem back-out kódu pro každou metodu, která mění stav objektu. Cílem není napsat zcela spolehlivý spravovaný kód, který se dokáže obnovit z chyb na každém místě pomocí náhradního kódu. To by byl náročný úkol s malou šancí na úspěch. Modul CLR (Common Language Runtime) nemůže snadno poskytnout dostatečně silné záruky pro spravovaný kód, aby bylo možné psát dokonalý kód. Na rozdíl od ASP.NET SQL Server používá pouze jeden proces, který nelze recyklovat, aniž by se databáze po nepřijatelnou dlouhou dobu zmenšovala.
Díky těmto slabším zárukám a provozu v jednom procesu je spolehlivost založena na ukončení vláken nebo na recyklaci domén aplikací v případě potřeby a přijetím preventivních opatření, aby se zajistilo, že prostředky operačního systému, jako jsou popisovače nebo paměť, neunikají. I když je toto jednodušší omezení spolehlivosti, stále existuje významný požadavek na spolehlivost:
Nikdy nevracet prostředky operačního systému.
Identifikujte všechny spravované zámky ve všech podobách pro CLR.
Nikdy nepřerušujte sdílený stav domény mezi aplikacemi, což umožňuje AppDomain bezproblémovou funkčnost recyklace.
I když je teoreticky možné psát spravovaný kód pro zpracování ThreadAbortException, StackOverflowException a OutOfMemoryException výjimek, očekávat, že vývojáři napíší takový robustní kód v celé aplikaci, je nepřiměřené. Z tohoto důvodu mají výjimky mimo běžný provoz za následek ukončení spuštěného vlákna; a pokud ukončené vlákno upravovalo sdílený stav, což lze určit podle toho, zda vlákno drží zámek, pak AppDomain je odstraněn. Pokud je metoda, která upravuje sdílený stav, ukončena, stav bude poškozen, protože není možné napsat spolehlivý back-out kód pro aktualizace do sdíleného stavu.
V rozhraní .NET Framework verze 2.0 je jediným hostitelem, který vyžaduje spolehlivost, SQL Server. Pokud bude sestavení spuštěno na SQL Serveru, měli byste pro každou část tohoto sestavení provádět práci se spolehlivostí, i když jsou při spuštění v databázi zakázané konkrétní funkce. To je povinné, protože modul pro analýzu kódu zkoumá kód na úrovni sestavení a nedokáže odlišit zakázaný kód. Dalším aspektem programování SQL Serveru je, že SQL Server spouští veškeré úlohy v jednom procesu a AppDomain recyklace se používá k čištění všech prostředků, jako jsou paměť a systémové prostředky.
U back-out kódu nemůžete záviset na finalizátorech, destruktorech nebo try/finally blocích. Mohou být přerušeny nebo nevyzvané.
Asynchronní výjimky mohou být vyvolány v neočekávaných umístěních, možná při každé instrukci počítače: ThreadAbortException, StackOverflowException a OutOfMemoryException.
Spravovaná vlákna nemusí být nutně vlákna Win32 v SQL; můžou to být vlákna.
Sdílený stav s proměnlivou doménou v rámci celého procesu nebo mezi aplikacemi je velmi obtížné bezpečně měnit a měli byste se jim vyhnout, kdykoli je to možné.
V SQL Serveru nejsou podmínky nedostatku paměti vzácné.
Pokud knihovny hostované na SQL Serveru správně neaktualizují svůj sdílený stav, existuje vysoká pravděpodobnost, že se kód neobnoví, dokud se databáze nerestartuje. V některých extrémních případech je také možné, že proces SQL Serveru selže, což způsobí restartování databáze. Restartování databáze může vyřadit z provozu web nebo ovlivnit provoz společnosti, čímž poškodí dostupnost. Pomalý únik prostředků operačního systému, jako je paměť nebo popisovače, může způsobit, že server nakonec selže a nebude schopen alokovat popisovače bez možnosti obnovy, nebo se výkon serveru může postupně snížit, čímž se snižuje dostupnost aplikace zákazníka. Jasně chceme těmto scénářům zabránit.
Pravidla osvědčených postupů
Úvod se zaměřil na to, co kontrola kódu pro spravovaný kód, který běží na serveru, musí zachytit, aby se zvýšila stabilita a spolehlivost frameworku. Všechny tyto kontroly jsou obecně dobrou praxí a na serveru jsou naprostou nutností.
V případě zablokování nebo omezení prostředků SQL Server přeruší vlákno nebo ukončí AppDomain. V takovém případě je zaručeno, že se spustí pouze zpětný kód v oblasti omezeného spuštění (CER).
Použití SafeHandle k zabránění úniku prostředků
V případě AppDomain uvolnění nelze spoléhat na spuštění bloků nebo finalizátorů finally, takže je důležité provést abstrakci veškerého přístupu k prostředkům operačního systému prostřednictvím třídy SafeHandle, nikoli IntPtr, HandleRef nebo podobných tříd. ClR tak může sledovat a zavírat popisovače, které používáte, i v AppDomain případě roztrhání.
SafeHandle bude používat kritickou finalizační metodu, kterou CLR vždy spustí.
Popisovač operačního systému je uložen v bezpečném popisovači od momentu jeho vytvoření až do momentu jeho uvolnění. Neexistuje žádné okno, během kterého ThreadAbortException může dojít k úniku rukojeti. Vyvolání platformy navíc bude počítat referenční počet popisovače, což umožňuje detailní sledování životnosti popisovače, čímž se zabrání problému se zabezpečením s kolizní situací mezi Dispose a metodou, která aktuálně používá popisovač.
Většina tříd, které aktuálně mají finalizační metodu, aby jednoduše vyčistil popisovač operačního systému, už nebude potřebovat finalizátor. Místo toho bude finalizátor v odvozené SafeHandle třídě.
Všimněte si, že SafeHandle není náhradou za IDisposable.Dispose. Stále existují potenciální konflikty prostředků a výkonnostní výhody při explicitním uvolnění prostředků operačního systému. Stačí si uvědomit, že finally bloky, které explicitně uvolňují prostředky, se nemusí dokončit.
SafeHandle umožňuje implementovat vlastní ReleaseHandle metodu, která provádí práci uvolnění popisovače, například předání stavu rutině operačního systému pro uvolnění popisovače nebo uvolnění sady popisovačů v rámci smyčky. CLR zaručuje spuštění této metody. Je odpovědností autora ReleaseHandle implementace zajistit, že popisovač bude uvolněn za všech okolností. Pokud to neuděláte, dojde k úniku popisovače, což často vede k úniku nativních prostředků přidružených k popisovači. Proto je důležité strukturovat SafeHandle odvozené třídy tak, aby ReleaseHandle implementace nevyžaduje přidělení žádné prostředky, které nemusí být k dispozici v době vyvolání. Mějte na paměti, že je přípustné volat metody, které mohou selhat v rámci implementace ReleaseHandle, za předpokladu, že váš kód dokáže takové chyby zpracovat a splnit podmínky pro uvolnění nativního popisovače. Pro účely ladění má ReleaseHandle návratovou hodnotu Boolean, která může být nastavena na false v případě, že dojde ke katastrofické chybě, která brání uvolnění prostředku. Tím se aktivuje releaseHandleFailed MDA, pokud je povoleno, aby pomohl identifikovat problém. Nemá žádný vliv na běh programu jiným způsobem; ReleaseHandle nebude znovu zavolána pro stejný prostředek a dojde k úniku popisovače.
SafeHandle není vhodné v určitých kontextech. Vzhledem k tomu, že lze metodu ReleaseHandle spustit na GC finalizačním vláknem, všechny popisovače, které je třeba uvolnit na konkrétním vlákně, by neměly být zabaleny do SafeHandle.
Obálky volatelné za běhu (RCWs) mohou být vyčištěny CLR bez dalšího kódu. Pro kód, který používá platformní výzvy a zpracovává objekt COM jako IUnknown* nebo IntPtr, by měl být kód přepsán tak, aby používal RCW.
SafeHandle pro tento scénář nemusí být adekvátní kvůli možnosti nespravované metody vydané verze volání zpět do spravovaného kódu.
Pravidlo analýzy kódu
Slouží SafeHandle k zapouzdření prostředků operačního systému. Nepoužívejte HandleRef ani pole typu IntPtr.
Ujistěte se, že finalizační metody nemusí běžet, aby se zabránilo úniku prostředků operačního systému.
Pečlivě zkontrolujte finalizátory a ujistěte se, že i když neběží, nedojde k úniku důležitých prostředků operačního systému. Na rozdíl od normálního AppDomain uvolnění při spuštění aplikace v stabilním stavu nebo při vypnutí serveru, jako je SQL Server, se objekty nedokončí během náhlého AppDomain uvolnění. Ujistěte se, že při náhlém uvolnění nedochází k úniku prostředků, protože správnost aplikace nemůže být zaručena, ale je nezbytné udržet integritu serveru bez úniku prostředků. Použijte SafeHandle k uvolnění jakýchkoli prostředků operačního systému.
Ujistěte se, že klauzule finally nemusí běžet, aby se zabránilo úniku prostředků operačního systému.
finally klauzule není zaručeno, že se budou spouštět mimo CERs, což znamená, že vývojáři knihoven by se neměli spoléhat na kód v finally bloku k uvolnění nespravovaných prostředků. Použití SafeHandle je doporučené řešení.
Pravidlo analýzy kódu
Slouží SafeHandle k vyčištění prostředků operačního systému místo Finalize. Nepoužívejte IntPtr; použijte SafeHandle k zapouzdření prostředků. Pokud musí klauzule finally běžet, umístěte ji do CER.
Všechny zámky by měly projít stávajícím spravovaným kódem uzamčení.
CLR musí vědět, kdy je kód v zámku, aby mohl AppDomain zrušit namísto pouhého přerušení vlákna. Přerušení vlákna může být nebezpečné, protože data zpracovávaná vláknem mohou zůstat v nekonzistentním stavu. Proto musí být celý AppDomain recyklován. Důsledky selhání identifikace zámku mohou být zablokování nebo nesprávné výsledky. Použijte metody BeginCriticalRegion a EndCriticalRegion identifikujte oblasti uzamčení. Jedná se o Thread statické metody třídy, které se vztahují pouze na aktuální vlákno, což pomáhá zabránit jednomu vláknu v úpravách počtu zámků jiného vlákna.
Enter a Exit mají toto oznámení CLR integrováno, takže se doporučuje jejich použití stejně jako použití příkazu lock, který tyto metody používá.
Jiné mechanismy uzamčení, jako jsou spinlocky, a AutoResetEvent musí volat tyto metody, aby upozornily CLR, že vstupuje se do kritického oddílu. Tyto metody neberou žádné zámky; informují CLR, že kód se spouští v kritické sekci a přerušení vlákna může ponechat nekonzistentní sdílený stav. Pokud jste definovali vlastní typ zámku, například vlastní ReaderWriterLock třídu, použijte tyto metody počtu zámků.
Pravidlo analýzy kódu
Označte a identifikujte všechny zámky pomocí BeginCriticalRegion a EndCriticalRegion. Nepoužívejte CompareExchange, Incrementa Decrement ve smyčce. Neprovádějte platformové volání Win32 verzí těchto metod. Nepoužívejte Sleep ve smyčce. Nepoužívejte nestálé pole.
Kód pro vyčištění musí být v bloku finally nebo catch, nikoli následujícím po catch bloku.
Čistící kód by nikdy neměl následovat za blokem catch; měl by být v bloku finally nebo v catch samotném bloku. To by mělo být normální dobrý postup.
finally Blok je obecně upřednostňován, protože spouští stejný kód jak když je vyvolána výjimka, tak i když je normálně dosaženo konce try bloku. V případě neočekávané výjimky, například ThreadAbortException, kód vyčištění se nespustí. Všechny nespravované prostředky, které byste vyčistili, v finally by měly být ideálně zabalené do SafeHandle mechanismu, aby se zabránilo únikům. Všimněte si, že klíčové slovo C# using lze efektivně použít k odstranění objektů, včetně popisovačů.
I když AppDomain recyklace dokáže vyčistit prostředky ve vlákně finalizátoru, stále je důležité, aby byl kód čištění umístěn na správné místo. Všimněte si, že pokud vlákno obdrží asynchronní výjimku, aniž by drželo zámek, CLR se pokusí vlákno ukončit samo, aniž by muselo recyklovat AppDomain. Zajistit, aby byly prostředky uvolněny dříve než později, pomáhá tím, že zpřístupňuje více zdrojů a lépe spravuje jejich životnost. Pokud explicitně nezavřete popisovač k souboru v některé chybové cestě kódu, je třeba počkat na SafeHandle finalizátor, aby soubor vyčistil. Při dalším spuštění kódu se může stát, že pokus o přístup k tomu samému souboru selže, pokud finalizátor ještě neproběhl. Z tohoto důvodu je zajištění existence a správné funkčnosti čisticího kódu užitečné pro rychlejší a čistší zotavení ze selhání, i když to není nezbytně nutné.
Pravidlo analýzy kódu
Vyčištění kódu po catch musí být v bloku finally. Zařaďte volání, která se mají vyhodit do konečného bloku.
catch bloky by měly končit hodem nebo rethrow. I když se objeví výjimky, například při zjišťování, zda je možné navázat síťové připojení, kde můžete narazit na velké množství různých výjimek, měl by jakýkoli kód, který za normálních okolností vyžaduje zachycení řady výjimek, naznačovat, že je potřeba jej otestovat, zda bude úspěšný.
Process-Wide proměnlivý sdílený stav mezi doménami aplikace by se měl odstranit nebo použít oblast omezeného spuštění.
Jak je popsáno v úvodu, může být velmi obtížné napsat spravovaný kód, který monitoruje sdílený stav celého procesu napříč doménami aplikace spolehlivým způsobem. Sdílený stav v rámci celého procesu je jakýkoli druh datové struktury sdílené mezi doménami aplikace, ať už v kódu Win32, uvnitř CLR nebo ve spravovaném kódu pomocí vzdálené komunikace. Jakýkoli proměnlivý sdílený stav je velmi obtížné správně psát ve spravovaném kódu a jakýkoli statický sdílený stav může být proveden pouze s velkou opatrností. Pokud máte sdílený stav na úrovni celého procesu nebo počítače, najděte nějaký způsob, jak ho odstranit nebo chránit sdílený stav pomocí oblasti omezeného spuštění (CER). Mějte na paměti, že každá knihovna se sdíleným stavem, která není identifikována a opravena, může způsobit pád hostitele, jako je SQL Server, který vyžaduje čisté AppDomain vyložení.
Pokud kód používá objekt COM, vyhněte se sdílení tohoto objektu MODELU COM mezi doménami aplikace.
Zámky nefungují v celém procesu ani mezi doménami aplikace.
V minulosti se Enter použil příkaz lock k vytváření globálních zámků procesů. K tomu dochází například při uzamčení AppDomain dynamických tříd, jako jsou Type instance z nesdílených sestavení, objekty Thread, interní řetězce a některé řetězce sdílené mezi doménami aplikace přes vzdálenou komunikaci. Tyto zámky už nejsou platné pro celý proces. Pokud chcete zjistit přítomnost zámku domény interapplication pro celý proces, určete, jestli kód v rámci zámku používá jakýkoli externí, trvalý prostředek, například soubor na disku nebo případně databázi.
Mějte na paměti, že uzamčení v rámci AppDomain můžou způsobit problémy, pokud chráněný kód používá externí prostředek, protože tento kód může běžet současně napříč více doménami aplikace. Může to být problém při zápisu do jednoho souboru protokolu nebo vazby na soket pro celý proces. Tyto změny znamenají, že neexistuje žádný snadný způsob, jak pomocí spravovaného kódu získat globální zámek procesu, kromě použití pojmenované Mutex nebo Semaphore instance. Vytvořte kód, který se nespustí ve dvou doménách aplikace současně, nebo použijte Mutex třídy Semaphore. Pokud nelze změnit existující kód, nepoužívejte Win32 pojmenovaný mutex k dosažení této synchronizace, protože spuštění v režimu vláken znamená, že nelze zaručit, že stejné vlákno operačního systému získá a uvolní mutex. Aby modul CLR věděl o synchronizaci zámku kódu, je nutné použít spravovanou Mutex třídu nebo pojmenované ManualResetEvent, AutoResetEvent nebo Semaphore místo synchronizace zámku pomocí nespravovaného kódu.
Vyhněte se lock(typeof(MyType))
Soukromé a veřejné Type objekty ve sdílených sestaveních s pouze jednou kopií kódu sdíleného napříč všemi doménami aplikace také představují problémy. U sdílených sestavení existuje pouze jedna instance Type procesu, což znamená, že stejnou Type instanci sdílí více domén aplikace. Zámek instance Type ovlivní celý proces, nejen AppDomain. Pokud jeden AppDomain získá zámek na objektu Type, pak je toto vlákno náhle přerušeno a zámek se neuvolní. Tento zámek pak může způsobit zablokování jiných domén aplikace.
Dobrým způsobem použití zámků ve statických metodách je přidání statického interního synchronizačního objektu do kódu. Tuto možnost lze inicializovat v konstruktoru třídy, pokud existuje, ale pokud ne, lze ji inicializovat takto:
private static Object s_InternalSyncObject;
private static Object InternalSyncObject
{
get
{
if (s_InternalSyncObject == null)
{
Object o = new Object();
Interlocked.CompareExchange(
ref s_InternalSyncObject, o, null);
}
return s_InternalSyncObject;
}
}
Když pak vezmete zámek, použijte InternalSyncObject vlastnost k získání objektu k uzamčení. Vlastnost není nutné použít, pokud jste inicializovali interní synchronizační objekt v konstruktoru třídy. Kód inicializace zámku dvojité kontroly by měl vypadat jako v tomto příkladu:
public static MyClass SingletonProperty
{
get
{
if (s_SingletonProperty == null)
{
lock(InternalSyncObject)
{
// Do not use lock(typeof(MyClass))
if (s_SingletonProperty == null)
{
MyClass tmp = new MyClass(…);
// Do all initialization before publishing
s_SingletonProperty = tmp;
}
}
}
return s_SingletonProperty;
}
}
Poznámka k použití 'this' jako zámku
Obecně je přijatelné vzít zámek u jednotlivého objektu, který je veřejně přístupný. Pokud je však objekt jedním objektem, který může způsobit zablokování celého subsystému, zvažte také použití výše uvedeného vzoru návrhu. Například zámek na jednom SecurityManager objektu může způsobit zablokování v rámci AppDomain, což způsobí, že celé AppDomain bude nepoužitelné. Je vhodné nezamknout zámek u veřejně přístupného objektu tohoto typu. Zámek jednotlivé kolekce nebo pole by však obecně neměl být problémem.
Pravidlo analýzy kódu
Nepoužívejte zámky u typů, které se můžou používat napříč doménami aplikací nebo nemají silný smysl pro identitu. Nepovolávejte Enter na Type, MethodInfo, PropertyInfo, String, ValueType, Thread ani na žádný objekt, který je odvozen od MarshalByRefObject.
Odeberte volání GC.KeepAlive
Značné množství existujícího kódu buď nepoužívá KeepAlive, když by mělo, nebo ho používá, když to není vhodné. Po převodu na SafeHandle třídy nemusí volat KeepAlive, za předpokladu, že nemají finalizátor, ale spoléhají na SafeHandle k finalizaci popisovačů operačního systému. I když může být výkonový náklad na zachování hovoru KeepAlive zanedbatelný, vnímání, že je volání KeepAlive buď nezbytné nebo dostatečné k vyřešení problému životnosti, který už nemusí existovat, ztěžuje údržbu kódu. Při použití volatelných obálek CLR pro meziprocesní komunikaci COM (RCWs) je však kódem stále vyžadováno KeepAlive.
Pravidlo analýzy kódu
Odeberte KeepAlive.
Použití atributu HostProtection
HpA HostProtectionAttribute (HPA) umožňuje použití deklarativních akcí zabezpečení k určení požadavků na ochranu hostitele, což hostiteli umožňuje zabránit, aby i plně důvěryhodný kód volal určité metody, které jsou pro daného hostitele nevhodné, jako například Exit nebo Show, v případě SQL Serveru.
HpA ovlivňuje pouze nespravované aplikace, které hostují modul CLR (Common Language Runtime) a implementují ochranu hostitelů, jako je SQL Server. Při použití má akce zabezpečení za následek vytvoření požadavku na propojení na základě prostředků hostitele, které třída nebo metoda zveřejňuje. Pokud je kód spuštěn v klientské aplikaci nebo na serveru, který není chráněn hostitelem, atribut "zmizí"; není zjištěn, a proto není použit.
Důležité
Účelem tohoto atributu je prosadit pokyny programovacího modelu specifické pro hostitele, nikoli charakter chování bezpečnosti. I když se poptávka po propojení používá ke kontrole shody s požadavky programovacího modelu, nejedná se HostProtectionAttribute o oprávnění zabezpečení.
Pokud hostitel nemá požadavky na programovací model, nedojde k požadavkům propojení.
Tento atribut identifikuje následující:
Metody nebo třídy, které neodpovídají programovacímu modelu hostitele, ale jinak jsou neškodné.
Metody nebo třídy, které neodpovídají programovacímu modelu hostitele a mohly by vést k nestabilitě kódu uživatele spravovaného serverem.
Metody nebo třídy, které neodpovídají programovacímu modelu hostitele a mohou vést k deaktivaci samotného procesu serveru.
Poznámka:
Pokud vytváříte knihovnu tříd, kterou mají volat aplikace, jež se můžou spouštět v hostovaném chráněném prostředí, měli byste tento atribut použít u členů, kteří zpřístupňují HostProtectionResource kategorie prostředků. Členové knihovny tříd rozhraní .NET Framework s tímto atributem způsobují, že je kontrolován pouze bezprostřední volající. Člen knihovny musí stejným způsobem také zkontrolovat svého bezprostředního volajícího.
Další informace o HPA naleznete v HostProtectionAttribute.
Pravidlo analýzy kódu
Pro SQL Server musí být všechny metody používané k zavedení synchronizace nebo vlákna identifikovány s HPA. To zahrnuje metody, které sdílejí stav, jsou synchronizované nebo spravují externí procesy. Hodnoty HostProtectionResource , které mají vliv na SQL Server, jsou SharedState, Synchronizationa ExternalProcessMgmt. Všechny metody, které zpřístupňují všechny HostProtectionResource, by však měly být identifikovány HPA, nejen těmi, které používají prostředky ovlivňující SQL.
Nezablokujte neomezeně v nespravovaném kódu
Blokování v nespravovaném kódu místo spravovaného kódu může způsobit útok na dostupnost služby, protože CLR nemůže přerušit vlákno. Blokované vlákno zabraňuje CLR uvolnit AppDomain, přinejmenším bez provedení některých mimořádně nebezpečných operací. Blokování pomocí primitiv synchronizace Windows je jasný příklad něčeho, co nemůžeme povolit. Blokování volání ReadFile v rámci socketu by se mělo pokud možno vyhnout – v ideálním případě by rozhraní API systému Windows mělo poskytnout mechanismus pro operaci, jako je tato, aby vypršel časový limit.
Jakákoli metoda, která volá do nativního prostředí, by měla ideálně použít volání Win32 s rozumným konečným časovým limitem. Pokud má uživatel povoleno zadat časový limit, neměl by být uživatel povolen k zadání nekonečného časového limitu bez konkrétního oprávnění zabezpečení. Pokud metoda bude blokovat déle než ~10 sekund, musíte použít verzi, která podporuje vypršení časových limitů, nebo potřebujete další podporu CLR.
Tady je několik příkladů problematických rozhraní API. Kanály (anonymní i pojmenované) lze vytvořit s časovým limitem, ale je nezbytné, aby kód nikdy nevolal CreateNamedPipe ani WaitNamedPipe s NMPWAIT_WAIT_FOREVER. Navíc může dojít k neočekávanému blokování, i když je zadaný časový limit. Volání WriteFile na anonymním kanálu bude blokovat, dokud nebudou zapsány všechny bajty, což znamená, že pokud vyrovnávací paměť obsahuje nepřečtená data, volání WriteFile bude blokováno, dokud čtečka neuvolní místo ve vyrovnávací paměti kanálu. Sokety by vždy měly používat některé rozhraní API, které dodržuje mechanismus časového limitu.
Pravidlo analýzy kódu
Blokování bez vypršení časového limitu v nespravovaném kódu je útok na dostupnost služby. Neprovádějte volání platformy na WaitForSingleObject, WaitForSingleObjectEx, WaitForMultipleObjects, MsgWaitForMultipleObjects a MsgWaitForMultipleObjectsEx. Nepoužívejte NMPWAIT_WAIT_FOREVER.
Identifikujte jakékoliv vlastnosti STA-Dependent
Identifikujte veškerý kód, který používá jednovláknové apartmány modelu COM (STA). V procesu SQL Serveru jsou zakázána STA. Funkce, které závisí na CoInitialize, jako čítače výkonu nebo schránka, musí být v SQL Serveru zakázané.
Ujistěte se, že finalizátory jsou bez problémů se synchronizací.
V budoucích verzích rozhraní .NET Framework může existovat více vláken finalizátorů, což znamená, že finalizační metody pro různé instance stejného typu běží současně. Nemusí být plně bezpečné pro vlákna; systém uvolňování paměti zaručuje, že pro danou instanci objektu spustí finalizátor pouze jedno vlákno. Finalizační metody však musí být kódovány tak, aby se zabránilo závodním podmínkám a zásekům při souběžném spuštění na více různých instancích objektů. Při použití jakéhokoli externího stavu, jako je zápis do souboru protokolu, musí být problémy s vlákny zpracovány v finalizátoru. Nespoléhejte na finalizaci, abyste zajistili bezpečnost vláken. Nepoužívejte místní úložiště vlákna, spravované ani nativní, k uložení stavu ve vlákně finalizátoru.
Pravidlo analýzy kódu
Finalizátory by měly být bez problémů se synchronizací. Nepoužívejte statický proměnlivý stav v finalizátoru.
Pokud je to možné, vyhněte se nespravované paměti
Nespravovaná paměť může unikat, stejně jako popisovač operačního systému. Pokud je to možné, zkuste použít paměť v zásobníku pomocí stackallocu nebo připnutého spravovaného objektu, jako je pevný příkaz nebo GCHandle použití bajtu[]. Nakonec GC je vyčistí. Pokud však musíte přidělit nespravovanou paměť, zvažte použití třídy, která dědí z SafeHandle, aby obálkovala přidělení paměti.
Mějte na paměti, že existuje alespoň jeden případ, kdy SafeHandle není adekvátní. U volání metod modelu COM, které přidělují nebo uvolňují paměť, je běžné, že jedna knihovna DLL přidělí paměť pomocí CoTaskMemAlloc, a poté jiná knihovna DLL tuto paměť uvolní pomocí CoTaskMemFree. Použití SafeHandle na těchto místech by nebylo vhodné, protože se pokusí spojit životnost nespravované paměti s životností SafeHandle místo toho, aby umožnilo druhé knihovně DLL ovládat životnost paměti.
Prohlédněte si všechna použití catch(Exception)
Bloky zachycení, které zachytí všechny výjimky místo jedné konkrétní výjimky, teď zachytí také asynchronní výjimky. Prozkoumejte každý blok catch(Exception), vyhledejte žádné důležité prostředky uvolnění nebo kód pro návrat, které by mohly být vynechané, a také potenciálně nesprávné chování při samotném bloku catch při zpracování ThreadAbortException, StackOverflowException, nebo OutOfMemoryException. Všimněte si, že je možné, že tento kód může protokolovat nebo provádět určité předpoklady, že může zobrazit pouze určité výjimky nebo že při každé výjimce dojde k selhání z právě jednoho konkrétního důvodu. Tyto předpoklady mohou být potřeba aktualizovat tak, aby zahrnovaly ThreadAbortException.
Zvažte změnu všech míst, která zachytávají všechny výjimky na zachycení určitého typu výjimky, kterou očekáváte, že bude vyvolána, například FormatException z metod formátování řetězců. Tím se zabrání spuštění bloku catch na neočekávaných výjimkách a pomůže to zajistit, že kód neukryje chyby tím, že by zachytával neočekávané výjimky. Obecně platí, že nikdy nezpracovávejte výjimku v knihovním kódu (kód, který vyžaduje zachycení výjimky, může značit chybu návrhu v kódu, který voláte). V některých případech můžete chtít zachytit výjimku a vyvolat jiný typ výjimky, aby bylo možné poskytnout více dat. V tomto případě použijte vnořené výjimky, které ukládají skutečnou příčinu selhání ve InnerException vlastnosti nové výjimky.
Pravidlo analýzy kódu
Zkontrolujte všechny bloky catch ve spravovaném kódu, které zachytí všechny objekty nebo zachytí všechny výjimky. V jazyce C# to znamená označení příznakem jak pro catch{}, tak pro catch(Exception){}. Zvažte velmi konkrétní typ výjimky nebo zkontrolujte kód, abyste zajistili, že nebude fungovat špatně, pokud zachytí neočekávaný typ výjimky.
Nepředpokládejte, že spravované vlákno je vlákno Win32 – jedná se o fiber.
Použití místního úložiště spravovaného vlákna funguje, ale nemůžete použít nespravované místní úložiště vlákna nebo předpokládat, že se kód znovu spustí na aktuálním vlákně operačního systému. Neměňte nastavení, jako je národní prostředí vlákna. Nevolejte InitializeCriticalSection ani CreateMutex prostřednictvím volání platformy, protože vyžadují, aby vlákno operačního systému, které vstupuje do zámku, také zámek uvolnilo. Vzhledem k tomu, že to nebude případ použití vláken, nelze v SQL přímo použít kritické oddíly a mutexy Win32. Všimněte si, že spravovaná Mutex třída nezpracuje tyto problémy se spřažením vláken.
U spravovaného Thread objektu můžete bezpečně používat většinu stavu, včetně místního úložiště spravovaného vlákna a aktuální jazykové verze uživatelského rozhraní vlákna. Můžete také použít ThreadStaticAttribute, což umožňuje, aby byla hodnota existující statické proměnné přístupná pouze aktuálním spravovaným vláknem (toto je jiný způsob, jak provádět fiber local storage v CLR). Z důvodů programovacího modelu nelze změnit aktuální kulturu vlákna při běhu v prostředí SQL.
Pravidlo analýzy kódu
SQL Server běží v režimu vláken; nepoužívejte místní úložiště vlákna. Vyhněte se volání platformy TlsAlloc, TlsFree, TlsGetValue a TlsSetValue.
Umožnit SQL Serveru zpracovat zosobnění
Vzhledem k tomu, že zosobňování funguje na úrovni vlákna a SQL může běžet ve fiberovém režimu, spravovaný kód by neměl zosobňovat uživatele a neměl by volat RevertToSelf.
Pravidlo analýzy kódu
Nechte SQL Server zpracovat zosobnění. Nepoužívejte RevertToSelf, ImpersonateAnonymousToken, DdeImpersonateClient, ImpersonateDdeClientWindow, ImpersonateLoggedOnUser, ImpersonateNamedPipeClient, ImpersonateSelf, RpcImpersonateClient, RpcRevertToSelf, RpcRevertToSelfEx nebo SetThreadToken.
Nevolejte Thread:Suspend
Schopnost pozastavit vlákno může vypadat jako jednoduchá operace, ale může způsobit zablokování. Pokud se vlákno držící zámek pozastaví druhým vláknem a druhé vlákno se pokusí vzít ten samý zámek, vznikne deadlock. Suspend může aktuálně narušovat zabezpečení, načítání tříd, vzdálené ovládání a reflektování.
Pravidlo analýzy kódu
Nevolejte Suspend. Zvažte použití skutečného synchronizačního primitiva, jako je například Semaphore nebo ManualResetEvent.
Ochrana kritických operací s využitím oblastí omezeného spouštění a kontraktů spolehlivosti
Při provádění komplexní operace, která aktualizuje sdílený stav nebo která musí deterministicky buď plně úspěšně, nebo zcela selhat, ujistěte se, že je chráněna omezenou oblastí provádění (CER). To zaručuje, že kód běží v každém případě, a to i v případě náhlého ukončení vlákna nebo náhlého AppDomain uvolnění.
CER je konkrétní try/finally blok, kterému bezprostředně předchází volání PrepareConstrainedRegions.
Tím dává kompilátoru just-in-time pokyn, aby připravil veškerý kód v bloku finally ještě před spuštěním try bloku. To zaručuje, že kód v posledním bloku je sestavený a bude se spouštět ve všech případech. V CER není neobvyklé mít prázdný try blok. Použití CER chrání před asynchronními přerušeními vláken a výjimkami při nedostatku paměti. Viz ExecuteCodeWithGuaranteedCleanup pro formu CER, která navíc zpracovává přetečení zásobníku při mimořádně rozsáhlém kódu.