Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Följande tillförlitlighetsregler är inriktade på SQL Server; De gäller dock även för alla värdbaserade serverprogram. Det är oerhört viktigt att servrar som SQL Server inte läcker resurser och inte tas ned. Detta kan dock inte göras genom att skriva back-out-kod för varje metod som ändrar ett objekts tillstånd. Målet är inte att skriva hundra procent tillförlitlig hanterad kod som kan hantera eventuella fel på varje plats med återställningskod. Det skulle vara en skrämmande uppgift med liten chans att lyckas. CLR (Common Language Runtime) kan inte enkelt ge tillräckligt starka garantier för hanterad kod för att göra det möjligt att skriva perfekt kod. Observera att till skillnad från ASP.NET använder SQL Server bara en process som inte kan återvinnas utan att ta bort en databas under en oacceptabelt lång tid.
Med dessa svagare garantier och körs i en enda process baseras tillförlitligheten på att avsluta trådar eller återanvända programdomäner vid behov och ta försiktighetsåtgärder för att förhindra att resurser från operativsystemet, såsom hanterare eller minne, läcker ut. Även med den här enklare tillförlitlighetsbegränsningen finns det fortfarande ett betydande tillförlitlighetskrav:
Läcka aldrig operativsystemresurser.
Identifiera alla hanterade lås i alla former till CLR.
Bryt aldrig det delade tillståndet för domäner mellan program, vilket gör AppDomain att återvinningen kan fungera smidigt.
Även om det teoretiskt sett är möjligt att skriva hanterad kod för att hantera ThreadAbortException, StackOverflowExceptionoch OutOfMemoryException undantag, är det orimligt att förvänta sig att utvecklare ska skriva sådan robust kod i hela programmet. Av den anledningen resulterar out-of-band-undantag i att den körande tråden avslutas; och om den avslutade tråden redigerade delat tillstånd, vilket kan avgöras av om tråden har ett lås, då avlägsnas AppDomain. När en metod som redigerar delat tillstånd avslutas är tillståndet skadat eftersom det inte går att skriva tillförlitlig back-out-kod för uppdateringar till delat tillstånd.
I .NET Framework version 2.0 är SQL Server den enda värd som kräver tillförlitlighet. Om sammansättningen ska köras på SQL Server bör du utföra tillförlitlighetsarbetet för varje del av sammansättningen, även om det finns specifika funktioner som är inaktiverade när de körs i databasen. Detta krävs eftersom kodanalysmotorn undersöker kod på sammansättningsnivå och inte kan särskilja inaktiverad kod. En annan SQL Server-programmeringsövervägning är att SQL Server kör allt i en process, och återställning AppDomain används för att rensa alla resurser, såsom minne och operativsystemhandtag.
Du kan inte vara beroende av slutbehandlare eller destruktorer eller try/finally
block för att återställa kod. De kan avbrytas eller inte anropas.
Asynkrona undantag kan genereras på oväntade platser, eventuellt varje datorinstruktion: ThreadAbortException, StackOverflowExceptionoch OutOfMemoryException.
Hanterade trådar är inte nödvändigtvis Win32-trådar i SQL. de kan vara fibrer.
Det är mycket svårt att ändra det delade tillståndet för processomfattande domäner eller domäner mellan program på ett säkert sätt och bör undvikas när det är möjligt.
Minnesbrist är inte ovanligt i SQL Server.
Om bibliotek som finns i SQL Server inte uppdaterar sitt delade tillstånd korrekt är det hög sannolikhet att koden inte återställs förrän databasen har startats om. I vissa extrema fall är det dessutom möjligt att detta kan leda till att SQL Server-processen misslyckas, vilket gör att databasen startas om. Omstart av databasen kan ta bort en webbplats eller påverka företagets verksamhet, vilket skadar tillgängligheten. En långsam läcka av operativsystemresurser som minne eller handtag kan leda till att servern till slut misslyckas med allokeringshandtag utan möjlighet till återställning, eller så kan servern långsamt försämras i prestanda och minska kundens programtillgänglighet. Det är uppenbart att vi vill undvika dessa scenarier.
Regler för bästa praxis
Introduktionen fokuserade på vad kodgranskningen för den hanterade koden som körs på servern skulle behöva fånga för att öka ramverkets stabilitet och tillförlitlighet. Alla dessa kontroller är bra praxis i allmänhet och ett absolut måste på servern.
Vid en deadlock eller resursbegränsning avbryter SQL Server en tråd eller stänger ner en AppDomain. När detta inträffar kommer endast återställningskod i en begränsad exekveringsregion (CER) att köras.
Använd SafeHandle för att undvika resursläckor
När det gäller en avlastning av AppDomain kan du inte lita på att finally
-block eller finalizers körs, så det är viktigt att abstrahera all åtkomst till operativsystemresurser genom SafeHandle-klassen istället för IntPtr, HandleRef eller liknande klasser. Detta gör att CLR kan spåra och stänga handtagen som du använder även i rivningsfallet AppDomain .
SafeHandle kommer att använda en kritisk finalisator som CLR alltid kommer att köra.
Operativsystemets handtag lagras i det säkra handtaget från det ögonblick det skapas tills det släpps. Det finns inget fönster där en ThreadAbortException kan uppstå för att läcka ett handtag. Dessutom kommer plattformsanrop att räkna referenser för handtaget, vilket möjliggör noggrann spårning av handtagets livslängd och förhindrar ett säkerhetsproblem med ett kapplöpningstillstånd mellan Dispose
och en metod som för närvarande använder handtaget.
De flesta klasser som för närvarande har en finalizer för att enkelt städa upp ett operativsystemhandtag kommer inte längre att behöva finalizern. I stället kommer finalizern att finnas i den SafeHandle härledda klassen.
Observera att SafeHandle inte är en ersättning för IDisposable.Dispose. Det finns fortfarande potentiella resurskonflikter och prestandafördelar med att frivilligt frigöra operativsystemresurser. Tänk bara på att finally
block som uttryckligen tar bort resurser kanske inte körs till slutförande.
SafeHandle gör att du kan implementera din egen ReleaseHandle-metod som utför arbetet för att frigöra handtaget, till exempel genom att överföra status till en frigöringsrutin för operativsystemshantering eller frigöra flera handtag i en slinga. CLR garanterar att den här metoden körs. Det är författarens ReleaseHandle ansvar att se till att handtaget släpps under alla omständigheter. Underlåtenhet att göra detta kommer att orsaka att handtaget läcker, vilket ofta resulterar i läckage av ursprungliga resurser associerade med handtaget. Därför är det viktigt att strukturera SafeHandle härledda klasser så att implementeringen ReleaseHandle inte kräver allokering av resurser som kanske inte är tillgängliga vid anrop. Observera att det är tillåtet att anropa metoder som kan misslyckas inom implementeringen av ReleaseHandle förutsatt att koden kan hantera sådana fel och slutföra kontraktet för att frigöra det interna handtaget. I felsökningssyfte ReleaseHandle har ett Boolean returvärde som kan anges till false
om ett oåterkalleligt fel påträffas som förhindrar publicering av resursen. Om du gör det aktiveras releaseHandleFailed MDA, om det är aktiverat, för att hjälpa dig att identifiera problemet. Det påverkar inte körtiden på något annat sätt; ReleaseHandle anropas inte igen för samma resurs och därför kommer handtaget att läcka.
SafeHandle är inte lämpligt i vissa sammanhang. Eftersom metoden kan köras på en finalizertråd bör inga handtag som måste frigöras på en viss tråd omslutas i en ReleaseHandle.
Runtime-anropsbara omslutningar (RCW) kan rensas av CLR utan ytterligare kod. För kod som använder plattform anropar och behandlar ett COM-objekt som ett IUnknown*
eller ett IntPtr, ska koden skrivas om för att använda en RCW.
SafeHandle kanske inte räcker för det här scenariot på grund av risken att en ohanterad release-metod anropar tillbaka till hanterad kod.
Kodanalysregel
Använd SafeHandle för att kapsla in operativsystemresurser. Använd inte HandleRef fält av typen IntPtr.
Se till att slutförarna inte behöver köras för att förhindra läckande operativsystemresurser
Granska dina finalizers noggrant för att säkerställa att även om de inte körs, läcker inte en kritisk operativsystemresurs. Till skillnad från en normal AppDomain avlastning när programmet körs i stabilt tillstånd eller när en server som SQL Server stängs av, slutförs inte objekt under en plötslig AppDomain avlastning. Se till att resurser inte läcker vid en plötslig avlastning, eftersom ett programs korrekthet inte kan garanteras, men serverns integritet måste upprätthållas genom att inte läcka resurser. Använd SafeHandle för att frigöra operativsystemresurser.
Se till att finally-satser inte behöver köras för att förhindra läckage av operativsystemresurser.
finally
-satser är inte garanterade att köras utanför CERs, vilket innebär att biblioteksutvecklare inte bör förlita sig på kod inom ett finally
-block för att frigöra ohanterade resurser. Att använda SafeHandle är den rekommenderade lösningen.
Kodanalysregel
Används SafeHandle för att rensa operativsystemresurser i stället för Finalize
. Använd inte IntPtr; använd SafeHandle för att kapsla in resurser. Om finally-satsen måste köras, placera den i en CER.
Alla lås ska gå igenom befintlig kod för hanterad låsning
CLR måste få veta när koden är i ett lås, för att den ska kunna riva ner AppDomain istället för att bara avsluta tråden. Det kan vara farligt att avbryta tråden eftersom data som drivs av tråden kan lämnas i ett inkonsekvent tillstånd. Därför måste hela AppDomain återvinns. Konsekvenserna av att inte identifiera ett lås kan vara antingen dödlägen eller felaktiga resultat. Använd metoderna BeginCriticalRegion och EndCriticalRegion för att identifiera låsregioner. De är statiska metoder i Thread klassen som endast gäller för den aktuella tråden, vilket förhindrar att en tråd redigerar en annan tråds låsantal.
Enter och Exit har det här CLR-meddelandet inbyggt, så det rekommenderas att använda dem samt att använda lås-instruktionen, som drar nytta av dessa metoder.
Andra låsningsmekanismer som spinnlås och AutoResetEvent måste anropa dessa metoder för att meddela CLR att ett kritiskt avsnitt har angetts. Dessa metoder tar inga lås; de informerar CLR om att koden körs i ett kritiskt avsnitt och att avbryta tråden kan göra delat tillstånd inkonsekvent. Om du har definierat din egen låstyp, till exempel en anpassad ReaderWriterLock klass, använder du dessa metoder för antal lås.
Kodanalysregel
Markera och identifiera alla lås med hjälp av BeginCriticalRegion och EndCriticalRegion. Använd inte CompareExchange, Incrementoch Decrement i en loop. Gör inte en plattformsanrop av Win32-varianterna av dessa metoder. Använd inte Sleep i en loop. Använd inte flyktiga fält.
Rensningskoden måste vara i ett finally- eller catch-block, inte efter ett catch-block
Rensningskoden bör aldrig följa ett catch
block. Den bör vara i ett finally
eller i catch
själva blocket. Detta bör vara en normal god praxis. Ett finally
block föredras vanligtvis eftersom det kör samma kod både när ett undantag utlöses och när slutet av try
blocket normalt påträffas. Om ett oväntat undantag utlöses, till exempel en ThreadAbortException, körs inte rensningskoden. Alla ohanterade resurser som du skulle rensa upp i en finally
bör helst omslutas i en SafeHandle för att förhindra läckor. Observera att nyckelordet C# using
kan användas effektivt för att ta bort objekt, inklusive handtag.
Även om AppDomain återanvändning kan rensa resurser på finalizertråden är det fortfarande viktigt att placera rensningskoden på rätt plats. Observera att om en tråd tar emot ett asynkront undantag utan att hålla ett lås, försöker CLR avsluta själva tråden utan att behöva återanvända AppDomain. Att se till att resurser rensas förr snarare än senare hjälper till genom att göra fler resurser tillgängliga och genom att bättre hantera livslängden. Om du inte uttryckligen stänger en referens till en fil i en felkodssökväg och istället väntar på SafeHandle att slutföraren ska städa upp den, kan nästa gång koden körs resultera i ett misslyckande att komma åt exakt samma fil om slutföraren inte redan har körts. Av den anledningen kommer att säkerställa att rensningskoden finns och fungerar korrekt hjälpa till att återhämta sig från fel mer ordentligt och snabbt, även om det inte är helt nödvändigt.
Kodanalysregel
Rensningskoden efter catch
måste finnas i ett finally
block. Placera anrop för att frigöra i ett finally-block.
catch
blocken ska sluta i ett kast eller ett återkast. Även om det kommer att finnas undantag, till exempel kod som identifierar om en nätverksanslutning kan upprättas där du kan få något av ett stort antal undantag, bör all kod som kräver fångst av ett antal undantag under normala omständigheter ge en indikation på att koden bör testas för att se om den lyckas.
Process-Wide föränderligt delat tillstånd mellan programdomäner bör elimineras eller använda en begränsad körningsregion
Som beskrivs i introduktionen kan det vara mycket svårt att skriva hanterad kod som övervakar processomfattande delat tillstånd mellan programdomäner på ett tillförlitligt sätt. Delat processomfattande tillstånd är alla typer av datastrukturer som delas mellan programdomäner, antingen i Win32-kod, i CLR eller i hanterad kod med hjälp av remoting. Alla föränderliga delade tillstånd är mycket svåra att skriva korrekt i hanterad kod, och alla statiska delade tillstånd kan endast utföras med stor försiktighet. Om du har ett processomfattande eller datoromfattande delat tillstånd kan du hitta något sätt att eliminera det eller skydda det delade tillståndet med hjälp av en begränsad körningsregion (CER). Observera att alla bibliotek med delat tillstånd som inte identifieras och korrigeras kan orsaka att en värd, till exempel SQL Server, som kräver ren AppDomain lossning kraschar.
Om koden använder ett COM-objekt bör du undvika att dela COM-objektet mellan programdomäner.
Lås fungerar inte i hela processen eller mellan programdomäner.
Tidigare har Enter och låssatsen använts för att skapa globala processlås. Detta inträffar till exempel när man låser på AppDomain agila klasser, såsom Type instanser från icke-delade sammansättningar, Thread objekt, såsom internerade strängar och vissa strängar som delas mellan programdomäner med hjälp av fjärrkommunikation. Dessa lås är inte längre processomfattande. För att identifiera förekomsten av ett processomfattande interapplication-domänlås kontrollerar du om koden i låset använder någon extern, bevarad resurs, till exempel en fil på disken eller eventuellt en databas.
Observera att ett lås i en AppDomain kan orsaka problem om den skyddade koden använder en extern resurs eftersom koden kan köras samtidigt över flera programdomäner. Detta kan vara ett problem när du skriver till en loggfil eller bindning till en socket för hela processen. Dessa ändringar innebär att det inte finns något enkelt sätt att använda hanterad kod för att få ett process-globalt lås, förutom att använda en namngiven Mutex eller Semaphore instans. Skapa kod som inte körs i två programdomäner samtidigt eller använd klasserna Mutex eller Semaphore . Om befintlig kod inte kan ändras ska du inte använda en Win32-namngiven mutex för att uppnå den här synkroniseringen eftersom om du kör i fiberläge kan du inte garantera att samma operativsystemtråd hämtar och släpper en mutex. Du måste använda den hanterade Mutex klassen, eller en med namnet ManualResetEvent, AutoResetEventeller en Semaphore för att synkronisera kodlåset på ett sätt som CLR är medveten om i stället för att synkronisera låset med ohanterad kod.
Undvik lock(typeof(MyType))
Privata och offentliga Type objekt i delade sammansättningar med endast en kopia av koden som delas mellan alla programdomäner har också problem. För delade sammansättningar finns det bara en instans av en Type per process, vilket innebär att flera programdomäner delar exakt samma Type instans. Att låsa en Type instans innebär ett lås som påverkar hela processen, inte bara AppDomain. Om man AppDomain tar ett lås på ett Type objekt och tråden avbryts plötsligt, kommer låset inte att släppas. Det här låset kan sedan leda till att andra programdomäner blir låsta.
Ett bra sätt att ta lås i statiska metoder är att lägga till ett statiskt internt synkroniseringsobjekt i koden. Detta kan initieras i klasskonstruktorn om det finns en, men om inte kan den initieras så här:
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;
}
}
När du sedan tar ett lås använder du InternalSyncObject
egenskapen för att hämta ett objekt att låsa på. Du behöver inte använda egenskapen om du har initierat det interna synkroniseringsobjektet i klasskonstruktorn. Initieringskoden för tvåfaldig lås-kontroll bör se ut så här:
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;
}
}
En anteckning om lås(detta)
Det är i allmänhet acceptabelt att låsa ett enskilt objekt som är offentligt tillgängligt. Men om objektet är ett singleton-objekt som kan orsaka ett helt undersystem till dödläge bör du även överväga att använda designmönstret ovan. Till exempel kan ett lås på ett SecurityManager objekt orsaka ett dödläge inom AppDomain, vilket gör hela AppDomain oanvändbar. Det är bra att inte låsa ett offentligt tillgängligt objekt av den här typen. Ett lås på en enskild samling eller matris bör dock i allmänhet inte utgöra något problem.
Kodanalysregel
Lås inte på typer som kan användas i programdomäner eller som inte har en stark identitetskänsla. Anropa Enter inte ett Type, MethodInfo, PropertyInfo, String, ValueType, eller Thread, eller något objekt som härleds från MarshalByRefObject.
Ta bort GC. KeepAlive-anrop
En betydande mängd befintlig kod använder KeepAlive antingen inte när den borde eller använder den när det inte är lämpligt. Efter konvertering till SafeHandle behöver klasserna inte anropa KeepAlive, förutsatt att de inte har en finalizer men förlitar sig på SafeHandle för att avsluta operativsystemets hanterare. Prestandakostnaden för att behålla ett anrop till KeepAlive kan vara försumbar, men uppfattningen att ett anrop till KeepAlive antingen är nödvändigt eller tillräckligt för att lösa ett livstidsproblem som kanske inte längre finns gör koden svårare att underhålla. Men när du använder COM-interop-CLR-anropsbara omslag (RCWs) krävs KeepAlive fortfarande av koden.
Kodanalysregel
Ta bort KeepAlive.
Använda attributet HostProtection
HostProtectionAttribute (HPA) möjliggör användningen av deklarativa säkerhetsåtgärder för att fastställa värdskyddskrav, vilket gör att värden kan förhindra att även fullt betrodd kod anropar vissa metoder som är olämpliga för den givna värden, som Exit eller Show för SQL Server.
HPA påverkar endast ohanterade program som är värdar för den vanliga språkkörningen och implementerar värdskydd, till exempel SQL Server. När den tillämpas resulterar säkerhetsåtgärden i att ett länkbehov skapas baserat på de värdresurser som klassen eller metoden exponerar. Om koden körs i ett klientprogram eller på en server som inte är värdskyddad avdunstar attributet . det identifieras inte och tillämpas därför inte.
Viktigt!
Syftet med det här attributet är att tillämpa riktlinjer för värdspecifika programmeringsmodeller, inte säkerhetsbeteende. Även om en länkefterfrågan används för att söka efter överensstämmelse med programmeringsmodellkrav, HostProtectionAttribute är det inte en säkerhetsbehörighet.
Om värden inte har krav på programmeringsmodell uppstår inte länkkraven.
Det här attributet identifierar följande:
Metoder eller klasser som inte passar värdprogrammeringsmodellen, men som annars är godartade.
Metoder eller klasser som inte passar värdprogrammeringsmodellen och som kan leda till destabiliserande serverhanterad användarkod.
Metoder eller klasser som inte passar värdprogrammeringsmodellen och som kan leda till en destabilisering av själva serverprocessen.
Anmärkning
Om du skapar ett klassbibliotek som ska anropas av program som kan köras i en värdskyddad miljö bör du använda det här attributet för medlemmar som exponerar HostProtectionResource resurskategorier. Medlemmar i .NET Framework-klassbiblioteket med det här attributet gör att endast den omedelbara anroparen kontrolleras. Biblioteksmedlemmen måste också göra en kontroll av sin omedelbara uppringare på samma sätt.
Mer information om HPA finns i HostProtectionAttribute.
Kodanalysregel
För SQL Server måste alla metoder som används för att införa synkronisering eller trådning identifieras med HPA. Detta omfattar metoder som delar tillstånd, synkroniseras eller hanterar externa processer. Värdena HostProtectionResource som påverkar SQL Server är SharedState, Synchronizationoch ExternalProcessMgmt. Alla metoder som exponerar någon HostProtectionResource bör dock identifieras av en HPA, inte bara de som använder resurser som påverkar SQL.
Blockera inte på obestämd tid i ohanterad kod
Blockering i ohanterad kod i stället för i hanterad kod kan orsaka en överbelastningsattack eftersom CLR inte kan avbryta tråden. En blockerad tråd hindrar CLR från att lossa AppDomain, åtminstone utan att utföra några extremt osäkra åtgärder. Att blockera med en primitiv Windows-synkronisering är ett tydligt exempel på något som vi inte kan tillåta. Blockering vid ett anrop till ReadFile
på en socket bör undvikas om möjligt – helst bör Windows API:t tillhandahålla en mekanism för att en sådan åtgärd ska kunna avbrytas efter en tidsgräns.
Alla metoder som använder det inbyggda systemet bör helst använda ett Win32-anrop med en rimlig, begränsad tidsgräns. Om användaren tillåts ange tidsgränsen bör användaren inte tillåtas ange en oändlig tidsgräns utan någon specifik säkerhetsbehörighet. Om en metod blockeras i mer än ~10 sekunder måste du använda en version som stöder tidsgränser, eller så behöver du ytterligare CLR-stöd.
Här följer några exempel på problematiska API:er. Rör (både anonyma och namngivna) kan skapas med en timeout. Koden måste dock se till att den aldrig anropar CreateNamedPipe
eller WaitNamedPipe
med NMPWAIT_WAIT_FOREVER. Dessutom kan det uppstå oväntad blockering även om en tidsgräns har angetts. Att anropa WriteFile
ett anonymt rör blockeras tills alla byte har skrivits, vilket innebär att om bufferten har olästa data i den blockeras anropet WriteFile
tills läsaren har frigjort utrymme i rörets buffert. Sockets bör alltid använda vissa API:er som respekterar en timeout-mekanism.
Kodanalysregel
Blockering utan timeout i ohanterad kod är en överbelastningsattack. Utför inte plattformsanrop till WaitForSingleObject
, WaitForSingleObjectEx
, WaitForMultipleObjects
, MsgWaitForMultipleObjects
och MsgWaitForMultipleObjectsEx
. Använd inte NMPWAIT_WAIT_FOREVER.
Identifiera alla STA-Dependent funktioner
Identifiera all kod som använder COM-lägenheter med enkel tråd (STA). STA:er är inaktiverade i SQL Server-processen. Funktioner som är beroende av CoInitialize
, till exempel prestandaräknare eller urklippet, måste inaktiveras i SQL Server.
Se till att slutförarna är fria från synkroniseringsproblem
Det kan finnas flera finalizertrådar i framtida versioner av .NET Framework, vilket innebär att finalizers för olika instanser av samma typ körs samtidigt. De behöver inte vara helt trådsäkra; skräpinsamlaren garanterar att endast en tråd kör finaliseraren för en viss objektinstans. Finalizers måste dock kodas för att undvika konkurrensförhållanden och dödlägen när de körs samtidigt på flera olika objektinstanser. När du använder ett externt tillstånd, till exempel att skriva till en loggfil, måste trådproblem hanteras i en slutförare. Förlita dig inte på slutförande för att ge trådsäkerhet. Använd inte trådlokal lagring, hanterad eller intern, för att lagra tillstånd på finalizer-tråden.
Kodanalysregel
Finalizers måste vara fria från synkroniseringsproblem. Använd inte ett statiskt föränderligt tillstånd i en finalizer.
Undvik ohanterat minne om möjligt
Ohanterat minne kan läcka ut, precis som ett operativsystemhandtag. Om möjligt kan du försöka använda minnet på stacken med stackalloc eller ett fäst hanterat objekt, till exempel den fasta instruktionen eller en GCHandle med en byte[]. De GC städar så småningom upp dessa. Men om du måste allokera ohanterat minne bör du överväga att använda en klass som härleds från SafeHandle för att omsluta minnesallokeringen.
Observera att det finns minst ett fall där SafeHandle inte är tillräckligt. För COM-metodanrop som allokerar eller frigör minne är det vanligt att en DLL allokerar minne via CoTaskMemAlloc
en annan DLL frigör det minnet med CoTaskMemFree
. Det skulle vara olämpligt att använda SafeHandle på dessa platser eftersom det försöker koppla livslängden för det ohanterade minnet till livslängden SafeHandle för i stället för att låta den andra DLL-filen styra minnets livslängd.
Granska alla användningar av catch(Exception)
Catch-block som fångar alla undantag i stället för ett specifikt undantag fångar nu även de asynkrona undantagen. Granska varje catch(Exception)-block och leta efter bristande frigöring av viktiga resurser och backout-kod som kan förbises, samt möjligtvis felaktigt beteende i catch-blocket vid hantering av en ThreadAbortException, StackOverflowException eller OutOfMemoryException. Observera att det är möjligt att den här koden kan logga eller göra vissa antaganden om att den bara kan se vissa undantag, eller att när ett undantag inträffar misslyckades den av exakt en viss anledning. Dessa antaganden kan behöva uppdateras för att inkludera ThreadAbortException.
Överväg att ändra alla platser som fångar upp alla undantag till att fånga en viss typ av undantag som du förväntar dig kommer att genereras, till exempel en FormatException från strängformateringsmetoder. Detta förhindrar att catch-blocket körs på oväntade undantag och hjälper till att se till att koden inte döljer buggar genom att fånga oväntade undantag. Som en allmän regel, hantera aldrig ett undantag i bibliotekskod (kod som kräver att fånga ett undantag kan tyda på ett designfel i koden du anropar). I vissa fall kanske du vill fånga ett undantag och generera en annan undantagstyp för att tillhandahålla mer data. Använd kapslade undantag i det här fallet och lagra den verkliga orsaken till felet i InnerException egenskapen för det nya undantaget.
Kodanalysregel
Granska alla catch-block i hanterad kod som fångar alla objekt eller fångar alla undantag. I C# innebär det att flagga både catch
{} och catch(Exception)
{}. Överväg att göra undantagstypen mycket specifik eller granska koden för att säkerställa att den inte fungerar på ett dåligt sätt om den fångar upp en oväntad undantagstyp.
Anta inte att en hanterad tråd är en Win32-tråd – Det är en Fiber
Att använda hanterad trådlokal lagring fungerar, men du får inte använda ohanterad trådlokal lagring eller anta att koden kommer att köras på den aktuella operativsystemtråden igen. Ändra inte inställningar som trådens nationella inställningar. Anropa inte InitializeCriticalSection
eller CreateMutex
via plattformsanrop eftersom de kräver att den operativsystemstråd som går in i ett lås också lämnar låset. Eftersom detta inte kommer att vara fallet när du använder fibrer kan win32-kritiska avsnitt och mutexes inte användas direkt i SQL. Observera att den hanterade Mutex klassen inte hanterar dessa problem med trådtillhörighet.
Du kan på ett säkert sätt använda det mesta av tillståndet på ett hanterat Thread objekt, inklusive lokal lagring av hanterade trådar och trådens aktuella användargränssnittskultur. Du kan också använda ThreadStaticAttribute, vilket gör värdet för en befintlig statisk variabel endast tillgängligt av den aktuella hanterade tråden (det här är ett annat sätt att göra lokal fiberlagring i CLR). Av programmeringsmodellskäl kan du inte ändra den aktuella kulturen i en tråd när du kör i SQL.
Kodanalysregel
SQL Server körs i fiberläge; använd inte trådlokal lagring. Undvik att göra plattformsanrop till TlsAlloc
, TlsFree
, TlsGetValue
och TlsSetValue.
Låt SQL Server hantera förklädnad
Eftersom personifiering fungerar på trådnivå och SQL kan köras i fiberläge bör hanterad kod inte personifiera användare och bör inte anropa RevertToSelf
.
Kodanalysregel
Låt SQL Server hantera igenkänning. Använd inte RevertToSelf
, ImpersonateAnonymousToken
, DdeImpersonateClient
, ImpersonateDdeClientWindow
, ImpersonateLoggedOnUser
, ImpersonateNamedPipeClient
, ImpersonateSelf
, RpcImpersonateClient
, , RpcRevertToSelf
, RpcRevertToSelfEx
eller SetThreadToken
.
Anropa inte Tråd::Pausa
Möjligheten att pausa en tråd kan se ut som en enkel åtgärd, men det kan orsaka dödlägen. Om en tråd som håller ett lås pausas av en andra tråd och den andra tråden försöker ta samma lås uppstår ett dödläge. För närvarande kan Suspend störa säkerhet, klassinläsning, fjärrkommunikation och reflektion.
Kodanalysregel
Ring inte Suspend. Överväg i stället att använda en verklig synkroniseringsprimitiv, till exempel en Semaphore eller ManualResetEvent.
Skydda kritiska operationer med begränsade exekveringsområden och tillförlitlighetskontrakt
När du utför en komplex åtgärd som uppdaterar en delad status eller som måste deterministiskt antingen lyckas helt eller misslyckas fullständigt, se till att den skyddas av en konstruerad utföranderegion (CER). Detta garanterar att koden körs i alla fall, till och med en abrupt tråd abort eller en abrupt AppDomain avlastning.
En CER är ett visst try/finally
block som omedelbart föregås av ett anrop till PrepareConstrainedRegions.
Detta instruerar just-in-time-kompilatorn att förbereda all kod i det sista blocket innan blocket try
körs. Detta garanterar att koden i finally-blocket skapas och körs i alla fall. Det är inte ovanligt i en CER att ha ett tomt try
block. Att använda en CER skyddar mot asynkrona tråd aborter och out-of-memory-undantag. Se ExecuteCodeWithGuaranteedCleanup för en form av en CER som dessutom hanterar stacköverskridanden för extremt djup kod.