Spolehlivost – doporučené postupy

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, nevracely prostředky a nebyly přeneseny. 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 100% spolehlivý spravovaný kód, který se obnoví z chyb v každém umístění pomocí back-out 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žená na ukončení vláken nebo recyklaci domén aplikací v případě potřeby a provedením preventivních opatření k zajištění nevracení prostředků operačního systému, jako jsou popisovače nebo paměť. 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 formulářích modulu 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, StackOverflowExceptiona OutOfMemoryException výjimky, očekává, že vývojáři napíší takový robustní kód v celé aplikaci je nepřiměřeně. Z tohoto důvodu mají výjimky mimo pásmo za následek ukončení spuštěného vlákna; a pokud ukončené vlákno upravovalo sdílený stav, který lze určit, zda vlákno obsahuje zámek, pak AppDomain je uvolně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í všechno v jednom procesu a AppDomain recyklace se používá k čištění všech prostředků, jako jsou paměť a popisovače operačního systému.

U back-out kódu nemůžete záviset na finalizátorech nebo destruktory nebo try/finally blocích. Můžou být přerušené nebo nevolané.

Asynchronní výjimky mohou být vyvolány v neočekávaných umístěních, pravděpodobně všechny instrukce počítače: ThreadAbortException, StackOverflowExceptiona 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 pořídit web nebo ovlivnit provoz společnosti a poškodit 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 alokuje popisovače bez možnosti obnovení nebo může server pomalu snížit výkon a 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, by se musel zachytit, aby se zvýšila stabilita a spolehlivost architektury. Všechny tyto kontroly jsou obecně vhodné a na serveru musí být absolutní.

V případě zablokování nebo omezení prostředků SQL Server přeruší vlákno nebo přeruší AppDomain. V takovém případě je zaručeno, že se spustí pouze back-out kód v oblasti omezeného spuštění (CER).

Použití Sejf Handle k zabránění úniku prostředků

V případě uvolnění nelze záviset na finally spuštěných blocích nebo finalizátorech, takže je důležité abstraktovat veškerý přístup k prostředkům operačního AppDomain systému prostřednictvím SafeHandle třídy, nikoli IntPtrHandleRef, 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 okamžiku, kdy se vytvoří až do okamžiku, kdy se uvolní. Neexistuje žádné okno, během kterého ThreadAbortException může dojít k úniku rukojeti. Volání platformy navíc bude počítat popisovač, který umožňuje úzké sledování životnosti popisovače, což brání problému se zabezpečením s časovací podmínkou 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í kolize prostředků a výhody výkonu pro explicitní odstranění prostředků operačního systému. Stačí si uvědomit, že finally bloky, které explicitně odstraňují prostředky, se nemusí provést k dokončení.

SafeHandle umožňuje implementovat vlastní ReleaseHandle metodu, která provádí práci, aby uvolnila popisovač, například předání stavu obslužné rutině operačního systému, uvolnění rutiny nebo uvolnění sady popisovačů ve smyčce. CLR zaručuje spuštění této metody. Je odpovědností autora ReleaseHandle implementace, aby se zajistilo, ž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 možné volat metody, které mohou selhat v rámci implementace za předpokladu ReleaseHandle , že váš kód dokáže takové chyby zpracovat a dokončit kontrakt, aby uvolnil nativní popisovač. Pro účely ladění má návratovou Boolean hodnotu, ReleaseHandle která se může nastavit, pokud dojde ke false katastrofické chybě, která brání uvolnění prostředku. Tím se aktivuje mdA releaseHandleFailed , pokud je povoleno, aby pomohl identifikovat problém. Nemá žádný vliv na modul runtime jiným způsobem; ReleaseHandle nebude znovu volána pro stejný prostředek a v důsledku toho dojde k úniku popisovače.

SafeHandle není vhodné v určitých kontextech. Vzhledem k tomu, že metodu ReleaseHandle lze spustit na GC finalizační vlákno, všechny popisovače, které jsou nutné uvolnit na konkrétním vlákně SafeHandle, by neměly být zabaleny do .

Obálky s možností volatelné za běhu (RCWs) je možné vyčistit modulem CLR bez dalšího kódu. Pro kód, který používá volání platformy a zpracovává objekt COM jako objekt nebo IUnknown* , IntPtrby 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 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 v případě náhlého uvolnění prostředků nedošlo k úniku prostředků, protože správnost aplikace nemůže být zaručena, ale integrita serveru musí být udržována nevracením prostředků. Slouží SafeHandle k uvolnění všech 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 nejsou zaručeny, že se spouštějí mimo pravidla CERS, což vyžaduje, aby vývojáři knihoven nespoléhali na kód v finally rámci bloku, aby uvolnili nespravované prostředky. 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í.

MODUL CLR musí vědět, kdy je kód v zámku, aby věděl AppDomain , že místo toho, aby přerušil vlákno. Přerušení vlákna může být nebezpečné, protože data provozovaná vláknem mohou zůstat v nekonzistentním stavu. Proto musí být celý AppDomain recyklován. Důsledky selhání identifikace zámku můžou být vzájemné 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 mějte toto oznámení CLR integrované, takže jejich použití se doporučuje i použití příkazu lock, který tyto metody používá.

Jiné mechanismy uzamčení, jako jsou zámky číselníku, a AutoResetEvent musí volat tyto metody, aby upozorňují CLR, že je zadán kritický oddíl. Tyto metody nezabírají žádné zámky; informuje CLR, že kód se spouští v kritické části 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. Nevyvolávej platformu variant Win32 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 posledního nebo catchu, nikoli zachytávání.

Kód čištění by nikdy neměl následovat za catch blokem finally , měl by být v bloku nebo v catch samotném bloku. To by mělo být normální dobrý postup. finally Blok je obecně upřednostňovaný, protože spouští stejný kód jak při vyvolání výjimky, tak při normálním výskytu try konce 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, finally by měly být ideálně zabalené do nespravovaných prostředků, aby se zabránilo únikům SafeHandle informací. 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, je stále důležité umístit kód čištění na správné místo. Všimněte si, že pokud vlákno obdrží asynchronní výjimku bez podržení zámku, CLR se pokusí ukončit samotné vlákno bez nutnosti recyklace AppDomain. Zajištění toho, aby se prostředky vyčistily dříve než později, tím, že zpřístupní více prostředků, a lepší správou životnosti. Pokud explicitně nezavřete popisovač souboru v nějaké cestě s kódem chyby, počkejte na SafeHandle vyčištění finalizátoru, při příštím spuštění kódu může dojít k selhání pokusu o přístup ke stejnému souboru, pokud finalizátor ještě neběží. Z tohoto důvodu zajistíte, že kód čištění existuje a funguje správně, pomůže lépe se zotavit z selhání a rychle, i když to není nezbytně nutné.

Pravidlo analýzy kódu

Vyčistit kód po catch musí být v finally bloku. 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ž dojde k výjimkám, jako je například zjištění kódu, jestli je možné navázat síťové připojení, kde můžete získat libovolný z velkého počtu výjimek, měl by každý kód, který vyžaduje zachycení řady výjimek za normálních okolností, indikovat, že by měl být kód testován, aby zjistil, jestli bude úspěšný.

Proměnlivý sdílený stav v rámci celého procesu 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 jakákoli knihovna se sdíleným stavem, která není identifikována a opravena, by mohla způsobit chybu hostitele, jako je SQL Server, který vyžaduje čisté AppDomain uvolnění.

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 Enter se k vytváření globálních zámků procesů použil příkaz lock . K tomu dochází například při uzamčení AppDomain agilních tříd, jako Type jsou například instance z nesdílených sestavení, Thread objektů, interovaných řetězců a některých řetězců sdílených napříč doménami aplikace pomocí vzdálené komunikace. Tyto zámky už nejsou v celém procesu. 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 tyto třídy Semaphore . Pokud existující kód nelze změnit, nepoužívejte win32 pojmenovaný mutex k dosažení této synchronizace, protože spuštění v režimu vláken znamená, že nemůžete zaručit, že stejné vlákno operačního systému získá a uvolní mutex. Ke synchronizaci zámku kódu je nutné použít spravovanou Mutex třídu nebo pojmenovanou ManualResetEventAutoResetEventtřídu nebo Semaphore zámek kódu způsobem, o který modul CLR neví, namí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. Převzetí zámku instance Type přebírá zámek, který ovlivňuje celý proces, nejen AppDomain. Pokud jeden AppDomain vezme zámek objektu Type , pak se toto vlákno náhle přeruší, zámek se neuvolní. Tento zámek pak může způsobit zablokování jiných domén aplikace.

Dobrým způsobem, jak vzít zámky ve statických metodách, zahrnuje 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 o zámku (toto)

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. Zámek na jednom SecurityManager objektu může například způsobit zablokování v rámci AppDomain celého nepoužitelného objektu AppDomain . Je vhodné nezamknout zámek u veřejně přístupného objektu tohoto typu. Zámek jednotlivých kolekcí nebo polí by ale obecně neměl představovat problém.

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ávat EnterType, , MethodInfo, PropertyInfoThreadStringValueTypenebo jakýkoli objekt, který je odvozen od .MarshalByRefObject

Odeberte GC. KeepAlive – volání

Značné množství existujícího kódu se buď nepoužívá KeepAlive , když by měl, nebo ho používá, pokud není vhodný. Po převodu na SafeHandletřídy nemusí volat KeepAlive, za předpokladu, že nemají finalizační metodu, ale spoléhají na SafeHandle finalizaci popisovačů operačního systému. I když náklady na výkon uchování hovoru KeepAlive mohou být zanedbatelné, vnímání, že volání KeepAlive je 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í obálky CLR volatelné zprostředkovatele komunikace modelu COM (RCWs) KeepAlive je však stále vyžadován kódem.

Pravidlo analýzy kódu

Odebrat KeepAlive.

Použití atributu HostProtection

HpA HostProtectionAttribute (HPA) poskytuje použití deklarativních akcí zabezpečení k určení požadavků na ochranu hostitelů, což umožňuje hostiteli zabránit, aby dokonce plně důvěryhodný kód volal určité metody, které jsou nevhodné pro daného hostitele, jako ExitShow je nebo pro SQL Server.

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 "odpaří"; nebyla zjištěna, a proto nebyla použita.

Důležité

Účelem tohoto atributu je vynutit pravidla programovacího modelu specifického pro hostitele, nikoli chování zabezpečení. 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, která se má volat aplikacemi, které se můžou spouštět v hostitelském chráněném prostředí, měli byste tento atribut použít u členů, kteří zpřístupňují HostProtectionResource kategorie prostředků. Členy knihovny tříd rozhraní .NET Framework s tímto atributem způsobí, že bude kontrolován pouze bezprostřední volající. Člen knihovny musí také stejným způsobem zkontrolovat jeho 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 podprocesu 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, kteří používají prostředky ovlivňující SQL.

Neblokujte neurčitý blok 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 uvolnění AppDomain, alespoň bez provedení některých extrémně 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 na soket by se mělo v případě potřeby 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; kód však musí zajistit, aby nikdy volání 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 anonymního 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í zablokuje, WriteFile dokud čtečka neuvolní místo v 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í volání platformy , WaitForSingleObjectWaitForSingleObjectEx, WaitForMultipleObjects, MsgWaitForMultipleObjects, a MsgWaitForMultipleObjectsEx. Nepoužívejte NMPWAIT_WAIT_FOREVER.

Identifikace všech funkcí závislých na STA

Identifikujte veškerý kód, který používá jednovláknové apartmány modelu COM (STA). V procesu SQL Serveru jsou zakázané tokeny tokenů. Funkce, které závisí na CoInitializečítačích výkonu nebo schránce, musí být v rámci SQL Serveru zakázané.

Ujistěte se, že finalizační metody nejsou problémy 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 zcela bezpečné pro vlákno; systém uvolňování paměti zaručuje, že pro danou instanci objektu spustí finalizační metodu pouze jedno vlákno. Finalizační metody však musí být kódovány, aby se zabránilo podmínkám časování a zablokování 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

Finalizační metody musí 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 být nespravovaná, 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á je odvozena od SafeHandle zalomení přidělení paměti.

Mějte na paměti, že existuje alespoň jeden případ, kdy SafeHandle není adekvátní. U volání modelu COM, která přidělují nebo uvolní paměť, je běžné, že jedna knihovna DLL přidělí paměť prostřednictvím CoTaskMemAlloc jiné knihovny DLL uvolní tuto paměť s CoTaskMemFree. Použití SafeHandle na těchto místech by bylo nevhodné, protože se pokusí spojit životnost nespravované paměti s životností SafeHandle namísto povolení druhé knihovny DLL řídit životnost paměti.

Kontrola všech 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ředek uvolnění nebo backout kódu, který by mohl být vynechán, a také potenciálně nesprávné chování v samotném bloku catch pro zpracování ThreadAbortExceptionStackOverflowException, 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í všechny výjimky tak, aby zachytila určitý typ výjimky, kterou očekáváte, se vyvolá, například FormatException z metod formátování řetězců. Tím zabráníte spuštění bloku catch na neočekávaných výjimkách a pomůžete tak zajistit, že kód neskryje chyby zachycením neočekávaných výjimek. Obecně platí, že nikdy nezpracuje výjimku v kódu knihovny (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 i catch{}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ádáte, že spravované vlákno je vlákno Win32 – jedná se o vlákno

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 nevolejte volání platformy, protože vyžadují vlákno operačního systému, které zadává zámek, také ukončete zámek. 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 ThreadStaticAttributehodnotu existující statické proměnné přístupné pouze aktuálním spravovaným vláknem (to je další způsob, jak provádět místní úložiště vláken v CLR). Z důvodů programovacího modelu nelze změnit aktuální jazykovou verzi vlákna při spuštění v 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í volání platformy , TlsAllocTlsFree, TlsGetValuea , aTlsSetValue.

Umožnit SQL Serveru zpracovat zosobnění

Vzhledem k tomu, že zosobnění funguje na úrovni vlákna a SQL může běžet v režimu vláken, spravovaný kód by neměl zosobnit uživatele a neměl by volat RevertToSelf.

Pravidlo analýzy kódu

Nechte SQL Server zpracovat zosobnění. Nepoužívejte , , , DdeImpersonateClient, ImpersonateDdeClientWindow, ImpersonateLoggedOnUserRpcImpersonateClientImpersonateNamedPipeClientRpcRevertToSelfImpersonateSelf, , RpcRevertToSelfEx, nebo .SetThreadTokenImpersonateAnonymousTokenRevertToSelf

Nevolejte vlákno::Suspend

Schopnost pozastavit vlákno může vypadat jako jednoduchá operace, ale může způsobit zablokování. Pokud vlákno držící zámek se pozastaví druhým vláknem a druhé vlákno se pokusí provést stejný zámek, dojde k zablokování. Suspend může v současné době kolidovat se zabezpečením, načítáním tříd, vzdálené komunikace a reflexí.

Pravidlo analýzy kódu

Nevolejte Suspend. Zvažte místo toho použití primitivní skutečné synchronizace, jako SemaphoreManualResetEvent je například nebo .

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ě, dokonce i náhlé přerušení vlákna nebo náhlé AppDomain uvolnění.

CER je konkrétní try/finally blok bezprostředně před voláním PrepareConstrainedRegions.

Tím dává kompilátoru za běhu pokyn, aby před spuštěním try bloku připravil veškerý kód v bloku finally. 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 mimo paměť. Podívejte se ExecuteCodeWithGuaranteedCleanup na formu CER, která navíc zpracovává přetečení zásobníku pro nadlimitní kód.

Viz také