Denne artikkelen er en speilartikkel om maskinoversettelse, vennligst klikk her for å hoppe til originalartikkelen.

Utsikt: 3568|Svare: 2

[Kilde] ASP.NET forskjellen mellom ThreadStatic, CallContext og HttpContext

[Kopier lenke]
Publisert på 30.06.2023 20:34:10 | | |
Original:Innloggingen med hyperkoblingen er synlig.



Sammendrag:
Selv om du tror du vet hva du gjør, er det ikke trygt å lagre noe i et ThreadStaty-medlem, CallContext- eller Thread Local-lager i en ASP.Net applikasjon hvis verdien kan ha blitt satt på forhånd. for å Page_Load (f.eks. i IHttpModule eller sidekonstruktør), men under eller etter tilgang.

[Oppdatering: august 2008 Siden ganske mange fortsetter å lenke til denne artikkelen, føler jeg det er nødvendig å presisere at denne trådbytte-atferden skjer på et veldig spesifikt tidspunkt i sidens livssyklus, og ikke når det føles slik.] Formuleringen min etter å ha sitert Jeff Newsom er uheldig. Utover det er jeg veldig glad (og beæret) over å se flere referanser til denne artikkelen i designdiskusjoner om riktig håndtering av HttpContext. Jeg er glad folk synes det er nyttig. ]

Det er mye forvirring rundt hvordan man implementerer brukerspesifikke singletons i ASP.Net – det vil si at globale data er globale for bare én bruker eller forespørsel. Dette er ikke et uvanlig krav: publiser transaksjons-, sikkerhetskontekst- eller annen «global» data på ett sted, i stedet for å presse det inn i hvert metodekall, fordi loam-data gir en klarere (og mer lesbar) implementering. Men hvis du ikke er forsiktig, er dette et flott sted å skyte deg selv i foten (eller hodet). Jeg trodde jeg visste hva som foregikk, men det gjorde jeg ikke.

Det foretrukne alternativet vil være singelLagret i HttpContext.Current.Items, enkelt og sikkert, men relaterer singletonen det gjelder til bruken i den ASP.Net applikasjonen. Hvis singletonen feiler i forretningsobjektet ditt, er dette ikke ideelt. Selv om du pakker inn property access i en if-setning


Summary:
Selv om du tror du vet hva du gjør, er det ikke trygt å lagre noe i et ThreadStatic-medlem, CallContext eller Thread Local Storage i en ASP.Net applikasjon, hvis det er mulig at verdien kan settes opp før Page_Load (f.eks. i IHttpModule eller sidekonstruktør), men brukes under eller etter.

[Oppdatering: august 2008 Med tanke på det ganske store antallet som fortsetter å lenke til dette innlegget, føler jeg behov for å presisere at denne trådbytte-atferden skjer på et veldig spesifikt punkt på siden livssyklus og ikke når det føles som det. Formuleringen min etter Jef Newsons sitat var uheldig. Bortsett fra det, har jeg vært enormt takknemlig (og smigret) over hvor mange ganger jeg har sett dette innlegget sitert i designdiskusjoner om hvordan man skal håndtere HttpContext på riktig måte. Jeg er glad folk syntes det var nyttig.]

Det er mye forvirring rundt hvordan man skal implementere brukerspesifikke singletons i ASP.Net – det vil si globale data som bare er globale for én bruker eller forespørsel. Dette er ikke et uvanlig krav: å publisere transaksjoner, sikkerhetskontekst eller annen 'global' data på ett sted, i stedet for å presse det gjennom alle metodekall slik tramp-data kan føre til en renere (og mer lesbar) implementering. Men det er et flott sted å skyte seg selv i foten (eller hodet) hvis du ikke er forsiktig. Jeg trodde jeg visste hva som foregikk, men det gjorde jeg ikke.

Det foretrukne alternativet, å lagre singletonene dine i HttpContext.Current.Items, er enkelt og trygt, men knytter singletonen til bruk i en ASP.Net applikasjon. Hvis enkeltpersonen er nede i forretningsobjektene dine, er ikke dette ideelt. Selv om du pakker property-access inn i en if-setning

... da må du fortsatt referere til System.Web fra den assemblyen, som ofte encoragerer flere 'webby'-objekter på feil sted.

Alternativene er å bruke et statisk medlem [ThreadStatic], lokal trådlagring (som stort sett tilsvarer det samme), eller CallContext.

Problemene med [ThreadStatic] er godt dokumentert, men for å oppsummere:
Feltinitialisatorer skyter kun på den første tråden
ThreadStatic-data trenger eksplisitt opprydding (f.eks. i EndRequest), fordi selv om tråden er tilgjengelig, vil ikke ThreadStatic-dataene bli GC'et, så du kan lekke ressurser.
ThreadStatic-data er bare gyldig innenfor en forespørsel, fordi neste forespørsel kan komme inn i en annen tråd og hente noen andres data.
Scott Hanselman får det riktig, at ThreadStatic ikke fungerer godt med ASP.Net, men forklarer ikke fullt ut hvorfor.

Lagring i CallContext lindrer noen av disse problemene, siden konteksten dør ut på slutten av forespørselen og GC vil til slutt oppstå (selv om du fortsatt kan lekke ressurser til GC skjer hvis du oppbevarer engangsprodukter). I tillegg er CallContext måten HttpContext lagres på, så det må vel være greit, ikke sant? Uansett skulle man tro (som jeg gjorde) at så lenge man ryddet opp etter hver forespørsel, ville alt være fint:
"Hvis du initialiserer ThreadStatic-variabelen i starten av forespørselen og håndterer det refererte objektet korrekt på slutten av forespørselen, vil jeg risikere å påstå at ingenting galt vil skje

"Nå,Jeg kan ta feil. CLR kan slutte å hoste en tråd halvveis, serialisere stakken et sted, gi den en ny stakk, og la den begynne å kjøre。 Jeg tviler sterkt på dette. Jeg antar det er tenkelig at hyperthreading også kan gjøre ting vanskelig, men jeg tviler også på det. ”

Jeff Newsom


"Hvis du initialiserer en ThreadStatic-variabel i begynnelsen av en forespørsel, og du korrekt kvitter deg med det refererte objektet på slutten av forespørselen, vil jeg gå ut på en gren og påstå at ingenting Dårlig vil skje. Du er til og med kul mellom kontekster i samme AppDomain

"Nå, jeg kan ta feil her. CLR kan potensielt stoppe en administrert tråd midt i strømmen, serialisere ut stakken et sted, gi den en ny stakk, og la den begynne å kjøre. Jeg tviler sterkt på det. Jeg antar det er tenkelig at hypertråding også gjør ting vanskelig, men jeg tviler også på det."
Jef Newsom
Oppdatering: Dette er misvisende. Jeg skal forklare nærmere senere at denne trådbyttet bare kan skje mellom BeginRequest og Page_Load, men Jefs referanse skaper et veldig sterkt bilde som jeg ikke klarte å rette opp med en gang.

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å på et tidspunkt bestemmer ASP.NET at det er for mange I/O-tråder som behandler andre forespørsler. […] Den aksepterer kun forespørsler og setter dem i kø i et internt køobjekt innenfor ASP.NET kjøretid. Deretter, etter kø, vil I/O-tråden be om en arbeidertråd, og deretter vil I/O-tråden returnere til sin pool. […] Derfor lar ASP.NET den ansatte håndtere forespørselen. Den vil bringe den til ASP.NET kjøretid, akkurat som I/O-tråden ville gjort ved lav belastning.

Så på et tidspunkt bestemmer ASP.NET at det er for mange I/O-tråder som behandler andre forespørsler. [...] Den tar bare forespørselen og setter den i kø i dette interne køobjektet innenfor ASP.NET kjøretid. Deretter, etter at den er satt i kø, vil I/O-tråden be om en arbeidertråd, og deretter vil I/O-tråden bli returnert til sin pool. [...] Så ASP.NET vil la den arbeidstråden behandle forespørselen. Den tar den inn i ASP.NET kjøretid, akkurat som I/O-tråden ville gjort under lav belastning.

Jeg har alltid visst om det, men jeg tror det skjedde veldig tidlig og jeg brydde meg ikke. Men jeg ser ut til å ta feil. Vi hadde et problem i appen ASP.Net hvor en bruker klikket på en lenke etter å ha klikket på en annen lenke, og appen vår hadde et nullreferanseunntak i en av singletonene (jeg brukte CallContext i stedet for ThreadStatic for singletonen, men det viste seg å være irrelevant).

Jeg gjorde litt research på nøyaktig hvordan trådene ASP.Net fungerer, og fikk motstridende meninger forkledd som fakta (forespørsler er tråd-agile i forespørsler, mens forespørsler er forankret i tråder gjennom levetiden), så jeg kopierte problemet mitt i en testapplikasjon med en treg side (sov et sekund) og en rask side. Jeg klikker på lenken til den trege siden, og før siden kommer tilbake, klikker jeg på lenken til den raske siden. Resultatet (log4net-dump av det som skjer) blåste meg av banen.

Utdataene viser at for den andre forespørselen aktiveres BeginRequest-hendelsen og sidekonstruktøren i HttpModule-pipelinen på én tråd, men page_Load i en annen. Den andre tråden har allerede migrert HttpContext fra den første tråden, men ikke CallContext eller ThreadStatic (merk: siden selve HttpContext lagres i CallContext, betyr dette at ASP.Net eksplisitt migrerer HttpContext). La oss si det igjen:


Jeg har alltid visst om dette, men jeg antok at det skjedde tidlig nok i prosessen til at jeg ikke brydde meg. Det ser imidlertid ut til at jeg tok feil. Vi har hatt et problem i vår ASP.Net-app hvor brukeren klikker på én lenke rett etter å ha klikket på en annen, og appen vår eksploderer med et nullreferanseunntak for en av våre singletons (jeg bruker CallContext, ikke ThreadStatic for singletonen, men det viser seg at det ikke spiller noen rolle).

Jeg gjorde litt research på nøyaktig hvordan ASP fungerer. Nets tråding fungerer, og fikk motstridende meninger som utgir seg for å være fakta (forespørsler er tråd-agile innenfor en forespørsel mens forespørsler er festet til en tråd for hele livet), så jeg replikerte min Problem i en testapplikasjon med en treg side (sover et sekund) og en rask side. Jeg klikker på lenken for den langsomme siden, og før siden kommer tilbake, klikker jeg på lenken for den raske siden. Resultatene (en log4net-dump av hva som skjer) overrasket meg.

Det utdataene viser er at – for den andre forespørselen – utløses BeginRequest-hendelsene i HttpModule-pipelinen og sidekonstruktøren på én tråd, men Page_Load utløses på en annen. Den andre tråden har fått HttpContext migrert fra den første, men ikke CallContext eller ThreadStatic sine (NB: siden HttpContext selv lagres i CallContext, betyr dette ASP.Net er å eksplisitt migrere HttpContext over). La oss bare stave dette ut igjen:

  • Trådbytte skjer etter at IHttpHandler er opprettet
  • Etter at sidens feltinitialisator og konstruktør har kjørt
  • Etter alle BeginRequest-, AuthenticateRequest- og AquireSessionState-typer hendelser som brukes av Global.ASA/IHttpModules.
  • Kun HttpContext migreres til den nye tråden



Trådbyttet skjer etter at IHttpHandler er opprettet
Etter at sidens feltinitialisatorer og konstruktør kjøres
Etter alle BeginRequest-, AuthenticateRequest- og AquireSessionState-typer som dine Global.ASA / IHttpModules bruker.
Kun HttpContext migrerer til den nye tråden

Dette er veldig irriterende, for ut fra det jeg har sett betyr det at det eneste persistensalternativet for oppførselen til "ThreadStatic"-klassen i ASP.Net er å bruke HttpContext. Så for forretningsobjektene dine, enten fortsetter du å bruke if(HttpContext.Current!). =null) og System.Web-referanse (æsj), ellers må du lage en slags leverandørmodell for statisk persistens, som må settes opp før du får tilgang til disse singletonene. Dobbel kvalme.

Vennligst si at dette ikke er tilfelle.

Vedlegg: Full logg:


Dette er en stor plage, for så vidt jeg kan se betyr det at det eneste persistensalternativet for 'ThreadStatic'-lignende oppførsel i ASP.Net er å bruke HttpContext. Så for forretningsobjektene dine, enten sitter du fast med if(HttpContext.Current!=null) og System.Web-referansen (æsj), eller så må du komme opp med en slags leverandørmodell for din statisk persistens, som må settes opp før noen av disse singletonene blir tilgjengelig. Dobbel æsj.

Vær så snill, noen si at det ikke er sant.

Appendix: That log in full:
[3748] INFO 11:10:05,239 ASP. Global_asax. Application_BeginRequest() - START /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() - Sakte sidesøvn....

[2720] INFO 11:10:05,669 ASP. Global_asax. Application_BeginRequest() - START /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() - Sakte side våkner....
[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() - SLUTT /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() - SLUTT /ConcurrentRequestsDemo/FastPage.aspx
Nøkkelen er hva som skjer når FastPages Page_Load utløses. ThreadID-en er 4748, men ThreadID-en jeg lagret i ctors HttpContext er 2720. Hashkoden for logiske tråder er 1703, men hashkoden jeg lagrer i ctor er 1835. All data jeg lagret i CallContext mangler (selv dataene merket ILogicalThreadAffinnative), men HttpContext er fortsatt der. Som du kanskje forventer, er også min ThreadStatic borte.

Det viktigste er hva som skjer når FastPages Page_Load går i gang. ThreadID-en er 4748, men threadID-en jeg lagret i HttpContext i ctoren er 2720. Hashkoden for den logiske tråden er 1703, men den jeg lagret i ctoren er 1835. All data jeg lagret i CallContext er borte (selv den merket ILogicalThreadAffinative), men HttpContext er fortsatt der. Som forventet er også min ThreadStatic borte.
(Slutt)




Foregående:.NET/C#-samling Any() eller Count() som er raskere
Neste:Hvordan Lazy in C# holder trådene trygge
 Vert| Publisert på 30.06.2023 20:35:23 |
 Vert| Publisert på 02.07.2023 09:59:06 |
CallContext

Navnerom: System.Runtime.Remoting.Messaging
Type-fullt kvalifisert navn: System.Runtime.Remoting.Messaging.CallContext

Formål: Å tilby et sett attributter som sendes sammen med utførelseskodebanen, enkelt sagt: å gi mulighet til å sende data i utførelsesveien til trådet (flertrådet/enkelttrådet) kode.
metodebeskrivelseOm det kan brukes i et flertrådet miljø
SetDataLagre et gitt objekt og knytte det til et spesifisert navn.ikke
GetDataHent objektet med det angitte navnet fra System.Runtime.Remoting.Messaging.CallContextikke
LogicalSetDataLagre et gitt objekt i konteksten av et logisk kall og assosiere det med et spesifisert navn.være
LogicalGetDataHent objekter med spesifiserte navn fra den logiske kall-konteksten.være
FreeNamedDataSlotTøm dataplassene med det angitte navnet.være
HostContextFå eller sett vertskonteksten knyttet til den nåværende tråden. I nettmiljøet er det lik System.Web.HttpContext.Currentikke


Ansvarsfraskrivelse:
All programvare, programmeringsmateriell eller artikler publisert av Code Farmer Network er kun for lærings- og forskningsformål; Innholdet ovenfor skal ikke brukes til kommersielle eller ulovlige formål, ellers skal brukerne bære alle konsekvenser. Informasjonen på dette nettstedet kommer fra Internett, og opphavsrettstvister har ingenting med dette nettstedet å gjøre. Du må fullstendig slette innholdet ovenfor fra datamaskinen din innen 24 timer etter nedlasting. Hvis du liker programmet, vennligst støtt ekte programvare, kjøp registrering, og få bedre ekte tjenester. Hvis det foreligger noen krenkelse, vennligst kontakt oss på e-post.

Mail To:help@itsvse.com