Denna artikel är en spegelartikel om maskinöversättning, klicka här för att hoppa till originalartikeln.

Utsikt: 3568|Svar: 2

[Källa] ASP.NET skillnaden mellan ThreadStatic, CallContext och HttpContext

[Kopiera länk]
Publicerad på 2023-06-30 20:34:10 | | |
Original:Inloggningen med hyperlänken är synlig.



Sammanfattning:
Även om du tror att du vet vad du gör, är det inte säkert att lagra något i en ThreadStatic-medlem, CallContext eller Thread Local-butik i en ASP.Net applikation om värdet kan ha satts i förväg. att Page_Load (t.ex. i IHttpModule eller sidkonstruktör), men under eller efter åtkomst.

[Uppdatering: augusti 2008 Eftersom ganska många fortsätter att länka till denna artikel, känner jag att det är nödvändigt att förtydliga att detta trådbyte sker vid en mycket specifik punkt i sidans livscykel, och inte när det känns så.] Min formulering efter att ha citerat Jeff Newsom är olycklig. Annars är jag mycket glad (och smickrad) att se referenser till denna artikel flera gånger i designdiskussioner om korrekt hantering av HttpContext. Jag är glad att folk tycker det är användbart. ]

Det råder mycket förvirring kring hur man implementerar användarspecifika singletons i ASP.Net – det vill säga, global data är global för endast en användare eller förfrågan. Detta är inget ovanligt krav: publicera transaktions-, säkerhetskontext- eller annan "global" data på ett ställe, istället för att lägga in det i varje metodanrop, eftersom loam-data möjliggör en tydligare (och mer läsbar) implementation. Men om du inte är försiktig är detta en utmärkt plats att skjuta dig själv i foten (eller huvudet). Jag trodde att jag visste vad som pågick, men det gjorde jag inte.

Det föredragna alternativet är singletonLagrad i HttpContext.Current.Items, enkel och säker, men relaterar den aktuella singeltonen till dess användning i den ASP.Net tillämpningen. Om singelen misslyckas i ditt affärsobjekt är detta inte idealiskt. Även om du slår in tillgången till egendomen i en if-sats


Summary:
Även om du tror att du vet vad du gör, är det inte säkert att lagra något i en ThreadStatic-medlem, CallContext eller Thread Local Storage i en ASP.Net applikation, om det finns möjlighet att värdet kan vara inställt före Page_Load (t.ex. i IHttpModule eller sidkonstruktör) men nås under eller efter.

[Uppdatering: augusti 2008 Med tanke på det ganska stora antalet personer som fortsätter att länka till detta inlägg känner jag behov av att förtydliga att detta trådbytesbeteende sker vid en mycket specifik punkt på sidan livscykel och inte när det känns så. Min formulering efter Jef Newsons citat var olycklig. Bortsett från det har jag varit oerhört tillfredsställd (och smickrad) av hur många gånger jag sett detta inlägg citeras i designdiskussioner om hur man hanterar HttpContext på ett lämpligt sätt. Jag är glad att folk fann det användbart.]

Det råder mycket förvirring kring hur man implementerar användarspecifika singletons i ASP.Net – det vill säga global data som bara är global för en användare eller begäran. Detta är inget ovanligt krav: att publicera transaktioner, säkerhetskontext eller annan 'global' data på ett ställe, istället för att skicka det genom varje metodanrop som tramp-data kan leda till en renare (och mer läsbar) implementation. Men det är en utmärkt plats att skjuta sig själv i foten (eller huvudet) om man inte är försiktig. Jag trodde att jag visste vad som pågick, men det gjorde jag inte.

Det föredragna alternativet, att lagra dina singletons i HttpContext.Current.Items, är enkelt och säkert, men kopplar singletonen i fråga till att användas i en ASP.Net applikation. Om singeln finns nere i dina affärsobjekt är detta inte idealiskt. Även om du slår in property-access i en if-sats

... då måste du fortfarande referera till System.Web från den assemblyn, vilket tenderar att enkorera fler 'webby'-objekt på fel plats.

Alternativen är att använda en statisk medlem [ThreadStatic], Thread local storage (vilket i princip är samma sak), eller CallContext.

Problemen med [ThreadStatic] är väl dokumenterade, men för att sammanfatta:
Fältinitalisatorer skjuter endast på första tråden
ThreadStatic-data behöver explicit rensning (t.ex. i EndRequest), eftersom även om tråden är nåbar, kommer ThreadStatic-data inte att GC:as så du kan läcka resurser.
ThreadStatic-data är bara bra inom en förfrågan, eftersom nästa förfrågan kan komma in i en annan tråd och hämta någon annans data.
Scott Hanselman har rätt, att ThreadStatic inte fungerar bra med ASP.Net, men förklarar inte helt varför.

Lagring i CallContext lindrar vissa av dessa problem, eftersom kontexten dör ut i slutet av förfrågan och GC kommer så småningom att inträffa (även om du fortfarande kan läcka resurser tills GC inträffar om du förvarar engångssaker). Dessutom är CallContext hur HttpContext lagras, så det måste vara okej, eller hur?. Oavsett skulle man kunna tro (som jag gjorde) att om man städade efter sig själv efter varje förfrågan, så skulle allt vara bra:
"Om du initialiserar variabeln ThreadStatic i början av förfrågan och korrekt hanterar det refererade objektet i slutet av förfrågan, skulle jag riskera att påstå att inget dåligt kommer att hända

"Nu,Jag kan ha fel. CLR kan sluta hosta en tråd halvvägs, serialisera dess stack någonstans, ge den en ny stack och låta den börja köras。 Jag tvivlar starkt på detta. Jag antar att det är tänkbart att hypertrådning också skulle göra saker svåra, men jag tvivlar också på det. ”

Jeff Newsom


"Om du initierar en ThreadStatic-variabel i början av en förfrågan, och du korrekt gör dig av med det refererade objektet i slutet av förfrågan, kommer jag att våga påstå att inget dåligt kommer att hända. Du är till och med cool mellan kontexter i samma AppDomain

"Nu kan jag ha fel om det här. Clr:n skulle potentiellt kunna stoppa en hanterad tråd mitt i strömmen, serialisera dess stack någonstans, ge den en ny stack och låta den börja köras. Jag tvivlar starkt på det. Jag antar att det är tänkbart att hypertrådning också gör saker svåra, men jag tvivlar också på det."
Jef Newsom
Uppdatering: Detta är missvisande. Jag kommer att förklara mer senare att detta trådbyte bara kan ske mellan BeginRequest och Page_Load, men Jefs referens skapar en mycket kraftfull bild som jag misslyckades med att rätta direkt.

Update: This was the misleading bit. I do explain further later on that this thread-swap can only happen between the BeginRequest and the Page_Load, but Jef's quote creates a very powerful image I failed to immediately correct. My bad.
Så vid någon punkt bestämmer ASP.NET att det finns för många I/O-trådar som bearbetar andra förfrågningar. […] Den accepterar endast förfrågningar och köar dem i ett internt köobjekt inom ASP.NET körtid. Sedan, efter kö, kommer I/O-tråden att begära en worker-tråd, och därefter återvänder I/O-tråden till sin pool. […] Därför låter ASP.NET den arbetaren hantera begäran. Den kommer att ta den till ASP.NET körtid, precis som I/O-tråden skulle göra vid låg belastning.

Så vid någon punkt bestämmer ASP.NET att det finns för många I/O-trådar som bearbetar andra förfrågningar. [...] Den tar bara förfrågan och köar den i detta interna köobjekt inom ASP.NET runtime. Sedan, efter att den är i kö, kommer I/O-tråden att be om en worker-tråd, och sedan återlämnas I/O-tråden till sin pool. [...] Så ASP.NET låter den arbetstråden behandla förfrågan. Den tar den in i ASP.NET körtid, precis som I/O-tråden skulle göra under låg belastning.

Jag har alltid vetat om det, men jag tror det hände väldigt tidigt och jag brydde mig inte. Men jag verkar ha fel. Vi hade ett problem i ASP.Net app där en användare klickade på en länk efter att ha klickat på en annan länk, och vår app hade ett nullreferensundantag i en av singletonerna (jag använde CallContext istället för ThreadStatic för singletonen, men det visade sig vara irrelevant).

Jag gjorde lite research om exakt hur ASP.Net trådar fungerar, och fick motstridiga åsikter förklädda som fakta (förfrågningar är tråd-agila i förfrågningar, medan förfrågningar är förankrade i trådar under deras livstid), så jag kopierade mitt problem i en testapplikation med en långsam sida (sov en sekund) och en snabb sida. Jag klickar på länken till den långsamma sidan, och innan sidan återvänder klickar jag på länken till den snabba sidan. Resultatet (log4net-dump av vad som händer) blåste mitt sinne.

Utdata visar att för den andra förfrågan aktiveras BeginRequest-händelsen och sidkonstruktorn i HttpModule-pipelinen på en tråd, men page_Load i en annan. Den andra tråden har redan migrerat HttpContext från den första tråden, men inte CallContext eller ThreadStatic (notera: eftersom HttpContext lagras i CallContext betyder detta att ASP.Net uttryckligen migrerar HttpContext). Låt oss säga det igen:


Jag har alltid vetat om detta, men jag antog att det hände tillräckligt tidigt i processen för att jag inte skulle bry mig. Det verkar dock som att jag hade fel. Vi har haft ett problem i vår ASP.Net-app där användaren klickar på en länk precis efter att ha klickat på en annan, och vår app exploderar med ett nullreferensundantag för en av våra singletons (jag använder CallContext, inte ThreadStatic för singletonen, men det visar sig att det inte spelar någon roll).

Jag gjorde lite research om exakt hur ASP fungerar. Nets trådning fungerar, och jag fick motstridiga åsikter som utger sig för att vara fakta (förfrågningar är tråd-agila inom en förfrågan medan förfrågningar är fastnålade på en tråd under sin livstid) så jag replikerade min Problem i en testapplikation med en långsam sida (vilar en sekund) och en snabb sida. Jag klickar på länken för den långsamma sidan och innan sidan kommer tillbaka klickar jag på länken för den snabba sidan. Resultaten (en log4net-dump av vad som pågår) överraskade mig.

Vad utdata visar är att – för den andra förfrågan – aktiveras BeginRequest-händelserna i HttpModule-pipelinen och sidkonstruktorn på en tråd, men Page_Load aktiveras på en annan. Den andra tråden har fått HttpContext migrerad från den första, men inte CallContext eller ThreadStatics (obs: eftersom HttpContext själv lagras i CallContext betyder detta att ASP.Net är att explicit migrera HttpContext över). Låt oss bara förklara det här igen:

  • Trådväxling sker efter att IHttpHandler har skapats
  • Efter att sidans fältinitialisator och konstruktör körts
  • Efter alla BeginRequest-, AuthenticateRequest- och AquireSessionState-typer av händelser som används av Global.ASA/IHttpModules.
  • Endast HttpContext migreras till den nya tråden



Trådbytet sker efter att IHttpHandler har skapats
Efter att sidans fältinitialiserare och konstruktör körts
Efter alla BeginRequest-, AuthenticateRequest- och AquireSessionState-typer som dina Global.ASA / IHttpModules använder.
Endast HttpContext migrerar till den nya tråden

Detta är ett stort irritationsmoment eftersom det från vad jag sett innebär att det enda persistensalternativet för beteendet hos "ThreadStatic"-klassen i ASP.Net är att använda HttpContext. Så för dina affärsobjekt, antingen fortsätter du använda if(HttpContext.Current!). =null) och System.Web-referens (usch), eller så måste du ta fram någon form av leverantörsmodell för statisk persistens, som måste sättas upp innan man kan komma åt dessa singletons. Dubbel illamående.

Säg att så inte är fallet.

Bilaga: Fullständig logg:


Detta är ett stort irritationsmoment, för såvitt jag kan se betyder det att det enda persistensalternativet för 'ThreadStatic'-liknande beteende i ASP.Net är att använda HttpContext. Så för dina affärsobjekt är du antingen fast med if(HttpContext.Current!=null) och System.Web-referensen (usch) eller så måste du komma på någon form av leverantörsmodell för din statisk persistens, som måste upprättas innan någon av dessa singletons nås. Dubbel usch.

Snälla, någon säger att det inte är så.

Appendix: That log in full:
[3748] INFO 11:10:05,239 ASP. Global_asax. Application_BeginRequest() - BEGIN /ConcurrentRequestsDemo/SlowPage.aspx
[3748] INFO 11:10:05,239 ASP. Global_asax. Application_BeginRequest() - threadid=, threadhash=, threadhash(now)=97, calldata=
[3748] INFO 11:10:05,249 ASP. SlowPage_aspx.. ctor() - threadid=3748, threadhash=(cctor)97, threadhash(now)=97, calldata=3748, logicalcalldata=3748
[3748] INFO 11:10:05,349 ASP. SlowPage_aspx. Page_Load() - threadid=3748, threadhash=(cctor)97, threadhash(now)=97, calldata=3748, logicalcalldata=3748
[3748] INFO 11:10:05,349 ASP. SlowPage_aspx. Page_Load() - Långsam sidsömn....

[2720] INFO 11:10:05,669 ASP. Global_asax. Application_BeginRequest() - BEGIN /ConcurrentRequestsDemo/FastPage.aspx
[2720] INFO 11:10:05,679 ASP. Global_asax. Application_BeginRequest() - threadid=, threadhash=, threadhash(now)=1835, calldata=
[2720] INFO 11:10:05,679 ASP. FastPage_aspx.. ctor() - threadid=2720, threadhash=(cctor)1835, threadhash(now)=1835, calldata=2720, logicalcalldata=2720, threadstatic=2720

[3748] INFO 11:10:06,350 ASP. SlowPage_aspx. Page_Load() - Långsam sida vaknar....
[3748] INFO 11:10:06,350 ASP. SlowPage_aspx. Page_Load() - threadid=3748, threadhash=(cctor)97, threadhash(now)=97, calldata=3748, logicalcalldata=3748
[3748] INFO 11:10:06,350 ASP. Global_asax. Application_EndRequest() - threadid=3748, threadhash=97, threadhash(now)=97, calldata=3748
[3748] INFO 11:10:06,350 ASP. Global_asax. Application_EndRequest() - SLUT /ConcurrentRequestsDemo/SlowPage.aspx

[4748] INFO 11:10:06,791 ASP. FastPage_aspx. Page_Load() - threadid=2720, threadhash=(cctor)1835, threadhash(now)=1703, calldata=, logicalcalldata=, threadstatic=
[4748] INFO 11:10:06,791 ASP. Global_asax. Application_EndRequest() - threadid=2720, threadhash=1835, threadhash(now)=1703, calldata=
[4748] INFO 11:10:06,791 ASP. Global_asax. Application_EndRequest() - SLUT /ConcurrentRequestsDemo/FastPage.aspx
Nyckeln är vad som händer när FastPages Page_Load triggas. ThreadID är 4748, men ThreadID jag lagrade i ctors HttpContext är 2720. Hashkoden för logiska trådar är 1703, men hashkoden jag lagrar i ctor är 1835. All data jag lagrade i CallContext saknas (även data märkt ILogicalThreadAffinnative), men HttpContext finns fortfarande kvar. Som du kanske förväntar dig är min ThreadStatic också borta.

Det viktiga är vad som händer när FastPages Page_Load skjuter upp. ThreadID är 4748, men threadID:n jag lagrade i HttpContext i ctorn är 2720. Hashkoden för den logiska tråden är 1703, men den jag lagrade i ctor är 1835. All data jag lagrade i CallContext är borta (även den märkta ILogicalThreadAffinative), men HttpContext finns fortfarande kvar. Som väntat är min ThreadStatic också borta.
(Slut)




Föregående:.NET/C#-samling Any() eller Count() som är snabbare
Nästa:Hur Lazy in C# håller trådarna säkra
 Hyresvärd| Publicerad på 2023-06-30 20:35:23 |
 Hyresvärd| Publicerad på 2023-07-02 09:59:06 |
CallContext

Namnrymd: System.Runtime.Remoting.Messaging
Typ-fullt kvalificerat namn: System.Runtime.Remoting.Messaging.CallContext

Syfte: Att tillhandahålla en uppsättning attribut som skickas tillsammans med exekveringskodens väg, enkelt uttryckt: att möjliggöra att data skickas i exekveringsvägen för trådad (flertrådad/enkeltrådad) kod.
metodbeskrivningOm det kan användas i en multitrådad miljö
SetDataLagra ett givet objekt och koppla det till ett specificerat namn.inte
GetDataHämta objektet med det angivna namnet från System.Runtime.Remoting.Messaging.CallContextinte
LogicalSetDataLagra ett givet objekt i kontexten av ett logiskt anrop och koppla det till ett specificerat namn.vara
LogicalGetDataHämta objekt med angivna namn från det logiska anropssammanhanget.vara
FreeNamedDataSlotTöm dataplatserna med det angivna namnet.vara
HostContextHämta eller ställ in värdens kontext kopplad till den aktuella tråden. I webbmiljön är det lika med System.Web.HttpContext.Currentinte


Friskrivning:
All programvara, programmeringsmaterial eller artiklar som publiceras av Code Farmer Network är endast för lärande- och forskningsändamål; Ovanstående innehåll får inte användas för kommersiella eller olagliga ändamål, annars kommer användarna att bära alla konsekvenser. Informationen på denna sida kommer från internet, och upphovsrättstvister har inget med denna sida att göra. Du måste helt radera ovanstående innehåll från din dator inom 24 timmar efter nedladdning. Om du gillar programmet, vänligen stöd äkta programvara, köp registrering och få bättre äkta tjänster. Om det finns något intrång, vänligen kontakta oss via e-post.

Mail To:help@itsvse.com