Ta članek je zrcalni članek strojnega prevajanja, kliknite tukaj za skok na izvirni članek.

Pogled: 3568|Odgovoriti: 2

[Vir] ASP.NET razliko med ThreadStatic, CallContext in HttpContext

[Kopiraj povezavo]
Objavljeno na 30. 06. 2023 20:34:10 | | |
Izvirno:Prijava do hiperpovezave je vidna.



Povzetek:
Tudi če mislite, da veste, kaj počnete, ni varno shranjevati ničesar v ThreadStatic member, CallContext ali Thread Local v ASP.Net aplikaciji, če je bila vrednost morda vnaprej določena. do Page_Load (npr. v IHttpModule ali konstruktorju strani), vendar med ali po dostopu.

[Posodobitev: avgust 2008 Glede na to, da kar nekaj ljudi še vedno povezuje ta članek, menim, da je potrebno pojasniti, da se to vedenje zamenjave niti zgodi v zelo specifičnem trenutku življenjskega cikla strani in ne takrat, ko se tako zdi.] Moje besede po citiranju Jeffa Newsoma so nesrečne. Poleg tega sem zelo vesel (in počaščen), da se ta članek večkrat sklicuje na ta članek v razpravah o pravilnem ravnanju s HttpContextom. Vesel sem, da ljudje to najdejo uporabno. ]

Obstaja veliko zmede glede tega, kako implementirati uporabniško specifične singletone v ASP.Net – to pomeni, da so globalni podatki globalni le za enega uporabnika ali zahtevo. To ni redka zahteva: objaviti transakcijske, varnostne kontekstne ali druge "globalne" podatke na enem mestu, namesto da bi jih vključili v vsak klic metode, saj loam podatki omogočajo jasnejšo (in bolj berljivo) implementacijo. Če pa nisi previden, je to odličen kraj, da si ustreliš v nogo (ali glavo). Mislil sem, da vem, kaj se dogaja, a nisem.

Prednostna možnost bo singletonShranjeno v HttpContext.Current.Items, preprosto in varno, vendar povezuje omenjeni singleton z njegovo uporabo v ASP.Net aplikaciji. Če singleton v vašem poslovnem objektu odpove, potem to ni idealno. Tudi če dostop do lastnosti zavijete v if ukaz


Summary:
Tudi če mislite, da veste, kaj počnete, ni varno shranjevati ničesar v članu ThreadStatic, CallContext ali Thread Local Storage znotraj ASP.Net aplikacije, če obstaja možnost da je vrednost lahko nastavljena pred Page_Load (npr. v IHttpModule ali page constructorju), vendar dostopana med ali po tem.

[Posodobitev: avgust 2008 Glede na precej veliko število ljudi, ki še naprej povezujejo ta zapis, čutim potrebo po pojasnilu, da se to vedenje pri menjavi niti dogaja na zelo specifični točki na strani Življenjski cikel in ne kadarkoli se mu zdi tako. Moje besede po citatu Jefa Newsona so bile nesrečne. Kljub temu sem bil izjemno zadovoljen (in počaščen), kolikokrat sem videl, da je ta objava citirana v oblikovalskih razpravah o ustreznem ravnanju s HttpContextom. Vesel sem, da je ljudem to koristilo.]

Veliko je zmede glede uporabe uporabe uporabniško specifičnih enojnih podatkov v ASP.Net – torej globalnih podatkov, ki so globalni le za enega uporabnika ali zahtevo. To ni redka zahteva: objavljanje transakcij, varnostnega konteksta ali drugih 'globalnih' podatkov na enem mestu, namesto da bi jih potisnili skozi vsak klic metode, kot to lahko povzročijo tramp podatki. Čistejša (in bolj berljiva) izvedba. Vendar je to odličen kraj, da si ustreliš v nogo (ali glavo), če nisi previden. Mislil sem, da vem, kaj se dogaja, a nisem.

Prednostna možnost, shranjevanje singletonov v HttpContext.Current.Items, je preprosta in varna, vendar povezuje singleton z uporabo znotraj ASP.Net aplikacije. Če je singleton v vaših poslovnih predmetih izpadl, to ni idealno. Tudi če ovijete dostop do lastnosti v if stavek

... potem pa moraš še vedno referencirati System.Web iz tega asemblerja, ki običajno več 'webby' objektov označi na napačnem mestu.

Alternative so uporaba statičnega člana [ThreadStatic], lokalnega shranjevanja Thread (kar je v bistvu isto) ali CallContext.

Težave z [ThreadStatic] so dobro dokumentirane, a za povzetek:
Field initalizatorji se sprožijo šele v prvi niti
Podatki ThreadStatic potrebujejo eksplicitno čiščenje (npr. v EndRequest), ker čeprav je Thread dosegljiv, podatki ThreadStatic ne bodo zaščiteni z GC, zato lahko uhajate vire.
Podatki ThreadStatic so uporabni le znotraj zahteve, ker lahko naslednja zahteva pride v drugo nit in dobi podatke nekoga drugega.
Scott Hanselman ima prav, da ThreadStatic ne deluje dobro z ASP.Net, a ne pojasni povsem, zakaj.

Shranjevanje v CallContext omili nekatere od teh težav, saj kontekst na koncu zahteve preneha delovati in GC se bo sčasoma pojavil (čeprav lahko še vedno uhajate vire, dokler GC ne pride, če shranjuješ enkratne stvari). Poleg tega je CallContext način, kako se HttpContext shrani, torej mora biti v redu, kajne? Ne glede na to bi si mislili (kot sem jaz), da če na koncu vsake prošnje pospraviš za sabo, bo vse v redu:
"Če inicializiraš spremenljivko ThreadStatic na začetku zahteve in pravilno obravnavaš referencirani objekt na koncu zahteve, bi tvegal, da trdim, da se ne bo zgodilo nič slabega

"Zdaj,Morda se motim. CLR lahko preneha gostiti nit na pol poti, serializira njen sklad nekje, mu dodeli nov sklad in pusti, da začne izvajati。 Močno dvomim v to. Mislim, da je mogoče, da bi hipernavlakanje tudi otežilo stvari, a tudi dvomim. ”

Jeff Newsom


"Če na začetku zahteve inicializirate spremenljivko ThreadStatic in pravilno odstranite referencirani objekt na koncu zahteve, si bom upal trditi, da ni nič Slabo se bo zgodilo. Tudi med konteksti v istem AppDomainu si v redu

"Zdaj, morda se motim. CLR bi lahko potencialno ustavil upravljano nit sredi toka, serializiral njen sklad nekje, mu dodelil nov sklad in mu omogočil začetek izvajanja. Resno dvomim. Predvidevam, da je mogoče, da hiperniti tudi otežujejo stvari, a tudi v to dvomim."
Jef Newsom
Posodobitev: To je zavajajoče. Kasneje bom podrobneje pojasnil, da se ta menjava niti lahko zgodi le med BeginRequest in Page_Load, vendar Jefova referenca ustvari zelo močno podobo, ki je nisem takoj popravil.

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.
Tako ASP.NET nekje odločili, da je preveč I/O niti, ki obdelujejo druge zahteve. […] Sprejema le zahteve in jih postavlja v vrsto v notranji objekt čakalne vrste znotraj ASP.NET izvajanja. Nato po vrstnem redu I/O nit zahteva delovno nit, nato pa se I/O nit vrne v svoj bazen. […] Zato bo ASP.NET prepustil temu delavcu, da obravnava zahtevo. To ga bo pripeljalo do ASP.NET runtime, tako kot I/O nit pri nizki obremenitvi.

Tako ASP.NET nekje ugotovi, da je preveč vhodno-izhodnih niti, ki obdelujejo druge zahteve. [...] Preprosto vzame zahtevo in jo postavi v vrsto v ta notranji objekt čakalne vrste znotraj ASP.NET izvajanja. Ko je ta nit v vrsti, bo I/O nit zahtevala delovno nit, nato pa se I/O nit vrne v svoj bazen. [...] Tako ASP.NET delovna nit obdela zahtevo. Prenese ga v ASP.NET runtime, tako kot bi to storila I/O nit pod nizko obremenitvijo.

Vedno sem vedel za to, a mislim, da se je zgodilo zelo zgodaj in me ni zanimalo. Vendar se zdi, da se motim. Imeli smo težavo v ASP.Net aplikaciji, kjer je uporabnik kliknil na povezavo po kliku na drugo povezavo, naša aplikacija pa je imela v enem od singletonov izjemo za ničelno referenco (za singleton sem namesto ThreadStatic uporabil CallContext, a se je izkazalo, da ni pomembno).

Naredil sem nekaj raziskav o tem, kako točno delujejo ASP.Net niti, in dobil nasprotujoča si mnenja, prikrita kot dejstva (zahteve so v zahtevah agilne po nitih, medtem ko so zahteve zasidrane na niti skozi celotno življenjsko dobo), zato sem svojo težavo kopiral v testni aplikaciji s počasno stranjo (za trenutek sem spal) in hitro stranjo. Kliknem na povezavo do počasne strani in preden se stran vrne, kliknem na povezavo do hitre strani. Rezultat (log4net izpis dogajanja) me je popolnoma presenetil.

Izhod pokaže, da se za drugo zahtevo dogodek BeginRequest in konstruktor strani v cevovodu HttpModule sprožita na eni niti, page_Load pa na drugi. Druga nit je že migrirala HttpContext iz prve niti, ne pa CallContext ali ThreadStatic (opomba: ker je HttpContext sam shranjen v CallContextu, to pomeni, da ASP.Net eksplicitno migrira HttpContext). Ponovimo:


Vedno sem vedel za to, a sem predvideval, da se je zgodilo dovolj zgodaj v procesu, da mi ni bilo mar. Zdi se pa, da sem se motil. Imamo težavo v naši ASP.Net aplikaciji, kjer uporabnik klikne eno povezavo takoj po tem, ko klikne drugo, in naša aplikacija se razširi z izjemo ničelne reference za enega od naših singletonov (uporabljam CallContext, ne ThreadStatic za singleton, a izkazalo se je, da ni pomembno).

Naredil sem nekaj raziskav o tem, kako točno ASP. Netovo nitno delovanje deluje, a sem dobil nasprotujoča si mnenja – ki se pretvarjajo, da so dejstva (zahteve so v zahtevi agilne, medtem ko so zahteve pripete na nit za celotno življenjsko dobo), zato sem ponovil svoje Težava v testni aplikaciji s počasno stranjo (za trenutek zaspi) in hitro stranjo. Kliknem povezavo za počasno stran in preden se stran vrne, kliknem povezavo za hitro stran. Rezultati (log4net izpis dogajanja) so me presenetili.

Izhod pokaže, da se za drugo zahtevo dogodki BeginRequest v HttpModule cevovodu in konstruktor strani sprožijo na eni niti, medtem ko Page_Load sproži na drugi. Druga nit je imela HttpContext migriran iz prve, vendar ne CallContext ali ThreadStatic (opomba: ker je HttpContext sam shranjen v CallContext, to pomeni ASP.Net je eksplicitno migracijo HttpContexta čez). Poglejmo to še enkrat:

  • Preklapljanje niti se zgodi po ustvarjanju IHttpHandlerja
  • Po zagonu inicializatorja polja in konstruktorja strani
  • Po vseh dogodkih tipa BeginRequest, AuthenticateRequest, AquireSessionState, ki jih uporabljajo Global.ASA/IHttpModules.
  • Samo HttpContext se prenese v novo nit



Preklop niti se zgodi po tem, ko je IHttpHandler ustvarjen
Po zagonu polja in konstruktorja strani
Po vseh dogodkih tipa BeginRequest, AuthenticateRequest, AquireSessionState, ki jih uporabljajo vaši Global.ASA / IHttpModule.
Le HttpContext se preseli v novo nit

To je velika nadloga, ker po mojih izkušnjah to pomeni, da je edina možnost za obstojnost vedenja razreda "ThreadStatic" v ASP.Net uporaba HttpContext. Torej za poslovne objekte lahko še naprej uporabljaš if(HttpContext.Current!). =null) in referenco na System.Web (uf), ali pa morate razviti nekakšen model ponudnika za statično obstojnost, ki ga je treba nastaviti pred dostopom do teh singletonov. Dvojna slabost.

Prosim, povejte, da temu ni tako.

Priloga: Celoten dnevnik:


To je velika nadloga, ker kolikor vidim, to pomeni, da je edina možnost za obstojnost vedenja, podobnega 'ThreadStaticu', v ASP.Net uporaba HttpContext. Torej, za vaše poslovne objekte ste bodisi obtičali z if(HttpContext.Current!=null) in referenco System.Web (fuj), ali pa morate razviti nekakšen model ponudnika Statično obstojnost, ki jo bo treba nastaviti pred dostopom do katerega koli od teh singletonov. Dvojno fuj.

Prosim, naj nekdo reče, da ni res.

Appendix: That log in full:
[3748] INFORMACIJE 11:10:05,239 ASP. Global_asax. Application_BeginRequest() - ZAČETEK /ConcurrentRequestsDemo/SlowPage.aspx
[3748] INFORMACIJE 11:10:05,239 ASP. Global_asax. Application_BeginRequest() - threadid=, threadhash=, threadhash(zdaj)=97, calldata=
[3748] INFORMACIJE 11:10:05,249 ASP. SlowPage_aspx.. ctor() - threadid=3748, threadhash=(cctor)97, threadhash(zdaj)=97, calldata=3748, logicalcalldata=3748
[3748] INFORMACIJE 11:10:05,349 ASP. SlowPage_aspx. Page_Load() - threadid=3748, threadhash=(cctor)97, threadhash(zdaj)=97, calldata=3748, logicalcalldata=3748
[3748] INFORMACIJE 11:10:05,349 ASP. SlowPage_aspx. Page_Load() - Počasi spanje strani....

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

[3748] INFORMACIJE 11:10:06,350 ASP. SlowPage_aspx. Page_Load() - Počasi se prebuja stran....
[3748] INFORMACIJE 11:10:06,350 ASP. SlowPage_aspx. Page_Load() - threadid=3748, threadhash=(cctor)97, threadhash(zdaj)=97, calldata=3748, logicalcalldata=3748
[3748] INFORMACIJE 11:10:06,350 ASP. Global_asax. Application_EndRequest() - threadid=3748, threadhash=97, threadhash(zdaj)=97, calldata=3748
[3748] INFORMACIJE 11:10:06,350 ASP. Global_asax. Application_EndRequest() - KONEC /ConcurrentRequestsDemo/SlowPage.aspx

[4748] INFORMACIJE 11:10:06,791 ASP. FastPage_aspx. Page_Load() - threadid=2720, threadhash=(cctor)1835, threadhash(now)=1703, calldata=, logicalcalldata=, threadstatic=
[4748] INFORMACIJE 11:10:06,791 ASP. Global_asax. Application_EndRequest() - threadid=2720, threadhash=1835, threadhash(zdaj)=1703, calldata=
[4748] INFORMACIJE 11:10:06,791 ASP. Global_asax. Application_EndRequest() - KONEC /ConcurrentRequestsDemo/FastPage.aspx
Ključno je, kaj se zgodi, ko se sproži Page_Load FastPage. ThreadID je 4748, vendar je ThreadID, ki sem ga shranil v ctorjev HttpContext, 2720. Hash koda za logične niti je 1703, vendar je hash koda, ki jo shranjujem v ctor, 1835. Vsi podatki, ki sem jih shranil v CallContext, manjkajo (tudi podatki, označeni kot ILogicalThreadAffinnative), vendar je HttpContext še vedno tam. Kot bi pričakovali, je tudi moj ThreadStatic izginil.

Ključno je, kaj se zgodi, ko se sproži Page_Load FastPage. ThreadID je 4748, vendar je threadID, ki sem ga shranil v HttpContext v ctorju, 2720. Hash koda za logično nit je 1703, vendar je tista, ki sem jo shranil v ctor, 1835. Vsi podatki, ki sem jih shranil v CallContext, so izginili (tudi tisti označeni kot ILogicalThreadAffinative), vendar je HttpContext še vedno tam. Kot bi pričakovali, je tudi moj ThreadStatic izginil.
(Konec)




Prejšnji:.NET/C# zbirka Any() ali Count(), ki je hitrejša
Naslednji:Kako lenoba v C# varuje teme
 Najemodajalec| Objavljeno na 30. 06. 2023 20:35:23 |
 Najemodajalec| Objavljeno na 2. 07. 2023 09:59:06 |
CallContext

Imenski prostor: System.Runtime.Redáling.Messaging
Tip-popolnoma kvalificirano ime: System.Runtime.Redáling.Messaging.CallContext

Namen: Zagotoviti nabor atributov, ki se prenašajo skupaj s potjo izvršilne kode, preprosto povedano: omogočiti prenos podatkov po poti izvajanja nitne (večnitne/enonitne) kode.
metodaopisAli ga je mogoče uporabiti v večnitnem okolju
SetDataShranite določen objekt in ga povežite z določenim imenom.ne
GetDataPridobimo objekt z določenim imenom iz System.Runtime.Redáling.Messaging.CallContextne
LogicalSetDataShranite določen objekt v kontekstu logičnega klica in ga povežite z določenim imenom.biti
LogicalGetDataPridobimo objekte z določenimi imeni iz logičnega klicnega konteksta.biti
FreeNamedDataSlotIzpraznite podatkovne reže z navedenim imenom.biti
HostContextPridobite ali nastavite kontekst gostitelja, povezan s trenutno nitjo. V spletnem okolju je enaka System.Web.HttpContext.Currentne


Disclaimer:
Vsa programska oprema, programski materiali ali članki, ki jih izdaja Code Farmer Network, so namenjeni zgolj učnim in raziskovalnim namenom; Zgornja vsebina ne sme biti uporabljena v komercialne ali nezakonite namene, sicer uporabniki nosijo vse posledice. Informacije na tej strani prihajajo z interneta, spori glede avtorskih pravic pa nimajo nobene zveze s to stranjo. Zgornjo vsebino morate popolnoma izbrisati z računalnika v 24 urah po prenosu. Če vam je program všeč, podprite pristno programsko opremo, kupite registracijo in pridobite boljše pristne storitve. Če pride do kakršne koli kršitve, nas prosimo kontaktirajte po elektronski pošti.

Mail To:help@itsvse.com