Metodtips för tillförlitlighet
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 100 procent tillförlitlig hanterad kod som återställs från eventuella fel på varje plats med back-out-kod. 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 vidta försiktighetsåtgärder för att säkerställa att operativsystemresurser som handtag eller minne inte 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örningstråd som avslutas. och om den avslutade tråden redigerade delat tillstånd, vilket kan avgöras av om tråden har ett lås, AppDomain tas den bort. 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ägande är att SQL Server kör allt i en process, och AppDomain återvinning används för att rensa alla resurser, till exempel minne och operativsystemhandtag.
Du kan inte vara beroende av finalizers eller destructors eller try/finally
block för back-out-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.
Inför ett olåst lås eller en resursbegränsning avbryter SQL Server en tråd eller river en AppDomain. När detta inträffar kommer endast back-out-kod i en begränsad körningsregion (CER) att köras.
Använd Valv Handle för att undvika resursläckor
När det gäller en AppDomain avlastning kan du inte vara beroende av finally
att block eller finalizers körs, så det är viktigt att abstrahera all åtkomst till operativsystemresurser via SafeHandle klassen i stället IntPtrför , HandleRefeller liknande klasser. Detta gör att CLR kan spåra och stänga handtagen som du använder även i rivningsfallet AppDomain . SafeHandle använder en kritisk finalator 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 räknar plattformsanrop referensreferensen, vilket möjliggör noggrann spårning av handtagets livslängd, vilket förhindrar ett säkerhetsproblem med ett konkurrenstillstånd mellan Dispose
och en metod som för närvarande använder handtaget.
De flesta klasser som för närvarande har en finalator för att helt enkelt rensa ett operativsystemhandtag behöver inte slutföraren längre. 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 resurskonkurration och prestandafördelar för att uttryckligen ta bort 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 att skicka tillstånd till ett operativsystem som hanterar frigöringsrutin eller frigör en uppsättning handtag i en loop. 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. Om du inte gör det läcker handtaget, vilket ofta resulterar i läckage av interna resurser som är 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örningen på något annat sätt. ReleaseHandle anropas inte igen för samma resurs och därför kommer handtaget att läckas.
SafeHandle är inte lämpligt i vissa sammanhang. ReleaseHandle Eftersom metoden kan köras på en GC finalizertråd bör alla referenser som måste frigöras på en viss tråd inte omslutas i en SafeHandle.
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 tillräcklig för det här scenariot på grund av möjligheten att en ohanterad versionsmetod 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 slutligen inte satser behöver köras för att förhindra läckande operativsystemresurser
finally
-satser är inte garanterade att köras utanför cers, vilket kräver att biblioteksutvecklare inte förlitar 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 slutsatsen måste köras placerar du den i en CER.
Alla lås ska gå igenom befintlig kod för hanterad låsning
CLR måste veta när koden är i ett lås så att den vet att riva ner AppDomain i stället för att bara avbryta 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.
Enteroch Exit har detta CLR-meddelande inbyggt, så deras användning rekommenderas samt användning av lås-instruktionen, som använder 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 äntligen eller ett catch-block, inte efter en fångst
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 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 ett handtag till en fil i en felkodssökväg väntar du på SafeHandle att slutföraren ska rensa den. Nästa gång koden körs kan det hända att den inte försöker komma åt exakt samma fil om finalizern inte redan har körts. Av den anledningen kommer rensningskoden att finnas och fungera korrekt att hjälpa till att återställa från fel mer rent och snabbt, även om det inte är absolut nödvändigt.
Kodanalysregel
Rensningskoden efter catch
måste finnas i ett finally
block. Placera anrop för att ta bort i ett äntligen block. catch
blocken ska sluta i ett kast eller en återväxt. Ä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.
Processomfattande 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. Processomfattande delat 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 fjärrkommunikation. 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 Enter har låssatsen använts för att skapa globala processlås. Detta inträffar till exempel vid låsning på AppDomain agila klasser, till exempel instanser från icke-delade sammansättningar, Thread objekt, internerade strängar och vissa strängar som Type 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 tar ett lås som påverkar hela processen, inte bara AppDomain. Om man AppDomain tar ett lås på ett Type objekt avbryts inte tråden plötsligt. 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 dubbelkontroll 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å det ena SecurityManager objektet orsaka ett dödläge inom AppDomain vilket hela AppDomain oanvändbara. 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 Threadnågot objekt som härleds från MarshalByRefObject.
Ta bort GC. KeepAlive-anrop
En betydande mängd befintlig kod används KeepAlive antingen inte när den ska eller använder den när den inte är lämplig. När du har konverterat till SafeHandlebehöver klasserna inte anropa KeepAlive, förutsatt att de inte har en finalizer men förlitar sig på SafeHandle att slutföra operativsystemets referenser. 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 omslutningar (RCW) KeepAlive krävs fortfarande av kod.
Kodanalysregel
Ta bort KeepAlive.
Använda attributet HostProtection
HostProtectionAttribute (HPA) tillhandahåller användning av deklarativa säkerhetsåtgärder för att fastställa värdskyddskrav, vilket gör att värden kan förhindra att även fullständigt betrodd kod anropar vissa metoder som är olämpliga för den angivna värden, till exempel 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.
Kommentar
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 AppDomainta bort , å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 i ett anrop till ReadFile
på en socket bör undvikas om möjligt – helst bör Windows-API:et tillhandahålla en mekanism för att en åtgärd som denna ska överskrida tidsgränsen.
Alla metoder som anropar inbyggda 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 eventuella STA-beroende 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 CoInitialize
av , till exempel prestandaräknare eller Urklipp, 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 finalizern 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
Slutförarna 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 instruktioneneller 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 det 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 ingen viktig resurs som släpper eller backout-kod som kan hoppas över, samt potentiellt felaktigt beteende i själva catch-blocket för hantering av en ThreadAbortException, StackOverflowExceptioneller 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 hanterar aldrig ett undantag i bibliotekskoden (kod som kräver att du får ett undantag kan tyda på ett designfel i koden som 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 lokal lagring med hanterad tråd fungerar, men du kanske inte använder ohanterad lokal trådlagring eller antar att koden körs på den aktuella operativsystemtråden igen. Ändra inte inställningar som trådens nationella inställningar. Anropa InitializeCriticalSection
inte eller CreateMutex
via plattformsanrop eftersom de kräver att den operativsystemtråd som anger ett lås också avslutar 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 plattform anropar anrop till TlsAlloc
, TlsFree
, TlsGetValue
och TlsSetValue.
Låt SQL Server hantera personifiering
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 personifiering. 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. Suspend kan störa säkerhet, klassinläsning, fjärrkommunikation och reflektion för närvarande.
Kodanalysregel
Anropa Suspendinte . Överväg att använda en verklig synkroniseringsprimerare i stället, till exempel en Semaphore eller ManualResetEvent .
Skydda kritiska åtgärder med begränsade körningsregioner och tillförlitlighetsavtal
När du utför en komplex åtgärd som uppdaterar en delad status eller som måste deterministiskt antingen lyckas helt eller helt misslyckas, se till att den skyddas av en begränsad körningsregion (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 det sista 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 en form av en CER som dessutom hanterar stackspill för mycket djup kod.