Šis straipsnis yra veidrodinis mašininio vertimo straipsnis, spauskite čia norėdami pereiti prie originalaus straipsnio.

Rodinys: 3568|Atsakyti: 2

[Šaltinis] ASP.NET skirtumas tarp "ThreadStatic", "CallContext" ir "HttpContext"

[Kopijuoti nuorodą]
Paskelbta 2023-06-30 20:34:10 | | |
Originalus:Hipersaito prisijungimas matomas.



Suvestinė:
Net jei manote, kad žinote, ką darote, nėra saugu ką nors saugoti "ThreadStatic" naryje, "CallContext" arba "Thread Local" saugykloje ASP.Net programoje, jei reikšmė galėjo būti nustatyta iš anksto. Page_Load (pvz., IHttpModule arba puslapio konstruktoriuje), bet prieigos metu arba po jos.

[Atnaujinimas: 2008 m. rugpjūčio mėn. Atsižvelgiant į tai, kad nemažai žmonių ir toliau pateikia nuorodas į šį straipsnį, manau, kad būtina paaiškinti, kad šis siūlų apsikeitimo elgesys įvyksta labai konkrečiu puslapio gyvavimo ciklo momentu, o ne tada, kai jis atrodo toks.] Mano formuluotė pacitavus Jeffą Newsomą yra apgailėtina. Be to, esu labai laimingas (ir pamalonintas) matydamas nuorodas į šį straipsnį kelis kartus dizaino diskusijose apie tinkamą HttpContext tvarkymą. Džiaugiuosi, kad žmonėms tai naudinga. ]

Yra daug painiavos dėl to, kaip įdiegti konkrečius vartotojo singletonus ASP.Net - tai yra, globalūs duomenys yra globalūs tik vienam vartotojui ar užklausai. Tai nėra neįprastas reikalavimas: skelbti operacijų, saugumo konteksto ar kitus "globalius" duomenis vienoje vietoje, o ne stumti juos į kiekvieną metodo iškvietimą, nes priemolio duomenys leidžia aiškiau (ir lengviau skaitomai) įgyvendinti. Tačiau, jei nesate atsargūs, tai puiki vieta šaudyti sau į koją (arba galvą). Maniau, kad žinau, kas vyksta, bet nežinojau.

Tinkamiausias variantas bus viengubasSaugoma HttpContext.Current.Items, paprasta ir saugi, bet susiejant aptariamą singletoną su jo naudojimu ASP.Net paraiškoje. Jei viengubas nepavyksta jūsų verslo objekte, tai nėra idealu. Net jei nuosavybės prieigą įvyniojate į if sakinį


Summary:
Net jei manote, kad žinote, ką darote, nėra saugu saugoti ką nors ThreadStatic naryje, CallContext ar Thread vietinėje saugykloje ASP.Net programoje, jei yra galimybė kad reikšmė gali būti nustatyta prieš Page_Load (pvz., IHttpModule arba puslapio konstruktoriuje), bet pasiekiama per arba po jo.

[Atnaujinimas: Rugpjūtis 2008 Atsižvelgiant į gana didelį skaičių žmonių ir toliau nuorodą į šį pranešimą jaučiu poreikį paaiškinti, kad šis temų apsikeitimo elgesys vyksta labai konkrečiu tašku puslapyje gyvenimo ciklą, o ne tada, kai jis jaučiasi. Mano formuluotė po Jef Newson citatos buvo apgailėtina. Be to, buvau be galo patenkintas (ir pamalonintas) kiek kartų mačiau šį įrašą cituojamas dizaino diskusijose apie tinkamą elgesį su HttpContext. Džiaugiuosi, kad žmonėms tai buvo naudinga.]

Yra daug painiavos dėl to, kaip ASP.Net įdiegti konkrečius vartotojo vienetus, t. y. globalius duomenis, kurie yra globalūs tik vienam vartotojui ar užklausai. Tai nėra neįprastas reikalavimas: skelbti operacijas, saugumo kontekstą ar kitus "globalius" duomenis vienoje vietoje, o ne stumti juos per kiekvieną metodą, kaip gali padaryti tramp duomenys švaresnis (ir lengviau skaitomas) įgyvendinimas. Tačiau tai puiki vieta šaudyti sau į koją (ar galvą), jei nesate atsargūs. Maniau, kad žinau, kas vyksta, bet nežinojau.

Pageidautina parinktis, saugoti savo singletons HttpContext.Current.Items, yra paprasta ir saugi, tačiau susieja aptariamą singleton su naudojimu ASP.Net programoje. Jei vienišas jūsų verslo objektas yra žemyn, tai nėra idealu. Net jei nuosavybės prieigą įvyniosite į if sakinį

... tada jūs vis dar turite nuorodą System.Web iš to surinkimo, kuris linkęs įterpti daugiau "webby" objektų netinkamoje vietoje.

Alternatyvos yra naudoti [ThreadStatic] statinį narį, Thread vietinę saugyklą (kuri beveik prilygsta tam pačiam dalykui) arba CallContext.

[ThreadStatic] problemos yra gerai dokumentuotos, bet apibendrinant:
Lauko initalizeriai šaudo tik ant pirmosios gijos
ThreadStatic duomenis reikia aiškiai išvalyti (pvz., EndRequest), nes kol Thread yra pasiekiama, ThreadStatic duomenys nebus GC'd, todėl galite nutekėti išteklius.
ThreadStatic duomenys yra tik bet koks geras per užklausą, nes kita užklausa gali ateiti į kitą giją, ir gauti kažkieno duomenis.
Scottas Hanselmanas teisingai supranta, kad "ThreadStatic" nežaidžia gerai su ASP.Net, bet iki galo nepaaiškina, kodėl.

Saugykla CallContext palengvina kai kurias iš šių problemų, nes kontekstas miršta užklausos pabaigoje ir GC atsiras galiausiai (nors vis tiek galite nutekėti išteklius, kol GC atsitiks, jei jūs saugote vienkartinius daiktus). Be to, CallContext yra tai, kaip HttpContext saugomas, todėl jis turi būti gerai, tiesa?. Nepriklausomai nuo to, jūs manote, kad (kaip ir aš), kad su sąlyga, kad jūs išvalyti po savęs kiekvieno prašymo pabaigoje, viskas būtų gerai:
"Jei inicijuojate ThreadStatic kintamąjį užklausos pradžioje ir teisingai tvarkyti nurodytą objektą užklausos pabaigoje, aš rizikuočiau teigti, kad nieko blogo nenutiks

"Dabar,Galiu klysti. CLR gali nustoti talpinti giją įpusėjus, kažkur serijizuoti jos krūvą, suteikti jai naują krūvą ir leisti jai pradėti vykdyti。 Aš tuo labai abejoju. Manau, kad tai įmanoma, kad hyperthreading taip pat būtų sunku, bet aš taip pat abejoju. ”

Džefas Newsomas


"Jei inicijuojate ThreadStatic kintamąjį užklausos pradžioje, ir jūs tinkamai atsikratyti nurodyto objekto užklausos pabaigoje, aš ketinu išeiti ant galūnės ir teigti, kad nieko blogai nutiks. Jūs netgi šaunus tarp kontekstų tame pačiame "AppDomain"

"Dabar galiu klysti. CLR potencialiai galėtų sustabdyti valdomą giją, kažkur išleisti jos krūvą, suteikti jai naują krūvą ir leisti jai pradėti vykdyti. Aš rimtai abejoju. Manau, kad galima įsivaizduoti, kad hiperthreading taip pat apsunkina situaciją, bet aš taip pat tuo abejoju."
Džefas Newsomas
Atnaujinimas: Tai klaidinanti. Vėliau paaiškinsiu, kad šis apsikeitimas temomis gali įvykti tik tarp BeginRequest ir Page_Load, tačiau Jef nuoroda sukuria labai galingą vaizdą, kurio man nepavyko ištaisyti iš karto.

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.
Taigi, tam tikru momentu, ASP.NET nuspręsti, kad yra per daug I/O gijų apdoroti kitas užklausas. […] Jis priima užklausas ir įtraukia jas į vidinį eilės objektą tik ASP.NET vykdymo metu. Tada, po eilės, I/O gija paprašys darbinės gijos, o tada I/O gija grįš į savo telkinį. […] Todėl ASP.NET leis tam darbuotojui tvarkyti užklausą. Tai atneš jį į ASP.NET vykdymo laiką, kaip ir I/O gija esant mažai apkrovai.

Taigi tam tikru momentu ASP.NET nusprendžia, kad yra per daug I/O gijų, apdorojančių kitas užklausas. [...] Jis tiesiog priima užklausą ir įtraukia ją į eilę šiame vidiniame eilės objekte per ASP.NET vykdymo laiką. Tada, kai tai bus eilėje, I/O gija paprašys darbininko gijos, o tada I/O gija bus grąžinta į savo telkinį. [...] Taigi ASP.NET turės tą darbuotojo giją, apdoroti užklausą. Jis pereis į ASP.NET vykdymo laiką, kaip ir I/O gija esant mažai apkrovai.

Dabar aš visada žinojau apie tai, bet manau, kad tai įvyko labai anksti ir man nerūpėjo. Tačiau atrodo, kad klystu. Mes turėjome problemą ASP.Net programoje, kai vartotojas spustelėjo nuorodą spustelėjęs kitą nuorodą, ir mūsų programa turėjo nulinės nuorodos išimtį viename iš singletons (aš naudojau CallContext vietoj ThreadStatic singleton, bet jis pasirodė nesvarbus).

Aš padariau keletą tyrimų, kaip tiksliai ASP.Net gijos veikia, ir gavau prieštaringų nuomonių užmaskuotas kaip faktai (prašymai yra siūlai-judrus prašymus, o prašymai yra įtvirtinti gijų per jų gyvenimą), todėl aš nukopijavau savo problemą bandomąją programą su lėtu puslapiu (miega sekundę) ir greitas puslapis. Paspaudžiu nuorodą į lėtą puslapį, o prieš grįžtant puslapiui paspaudžiu nuorodą į greitą puslapį. Rezultatas (log4net sąvartynas apie tai, kas vyksta) susprogdino mano protą.

Išvestis rodo, kad antrosios užklausos atveju BeginRequest įvykis ir puslapio konstruktorius HttpModule sraute suveikia vieną giją, bet page_Load kitoje. Antroji gija jau perkėlė HttpContext iš pirmosios gijos, bet ne CallContext ar ThreadStatic (pastaba: kadangi pats HttpContext yra saugomas CallContext, tai reiškia, kad ASP.Net aiškiai perkelia HttpContext). Pakartokime dar kartą:


Dabar aš visada žinojau apie tai, bet maniau, kad tai įvyko pakankamai anksti, kad man nerūpėjo. Tačiau atrodo, kad klydau. Mes turėjome problemą mūsų ASP.Net programoje, kai vartotojas spustelėja vieną nuorodą tik spustelėjęs kitą, ir mūsų programa susprogdina su nulinės nuorodos išimtimi vienam iš mūsų singletons (aš naudoju CallContext ne ThreadStatic singleton, bet pasirodo, kad tai nesvarbu).

Aš padariau šiek tiek tyrimų apie tai, kaip tiksliai ASP. Net's threading darbai, ir gavo prieštaringų nuomonių-maskuojamas-kaip-faktas (prašymai yra siūlai-judrus per prašymą vs prašymai yra prisegti prie gijos jų gyvenimą), todėl aš atkartoti mano problema bandomojoje programoje su lėtu puslapiu (sekundę miega) ir greitu puslapiu. Paspaudžiu lėto puslapio nuorodą ir prieš grįžtant puslapiui paspaudžiu greito puslapio nuorodą. Rezultatai (log4net sąvartynas apie tai, kas vyksta) mane nustebino.

Išvestis rodo, kad antrosios užklausos atveju BeginRequest įvykiai HttpModule sraute ir puslapio konstruktorius suveikia vienoje gijoje, bet Page_Load suveikia kitoje. Antroji gija turėjo HttpContext perkeltas iš pirmojo, bet ne CallContext ar ThreadStatic's (NB: kadangi HttpContext yra saugomas CallContext, tai reiškia, kad ASP.Net yra aiškiai perkeliant HttpContext). Leiskite tik paaiškinti tai dar kartą:

  • Gijos perjungiamos sukūrus IHttpHandler
  • Paleidus puslapio lauko iniciatorių ir konstruktorių
  • Po bet kokių BeginRequest, AuthenticateRequest, AquireSessionState tipo įvykių, kuriuos naudoja Global.ASA/IHttpModules.
  • Tik HttpContext perkeliamas į naują giją.



Gijos perjungimas įvyksta sukūrus IHttpHandler
Paleidus puslapio lauko iniciatorius ir konstruktorius
Po bet kokių BeginRequest, AuthenticateRequest, AquireSessionState tipo įvykių, kuriuos naudoja jūsų Global.ASA / IHttpModules.
Tik HttpContext perkeliamas į naują giją.

Tai yra pagrindinis PITA, nes iš to, ką aš mačiau, tai reiškia, kad vienintelis patvarumo variantas elgesio "ThreadStatic" klasės ASP.Net yra naudoti HttpContext. Taigi savo verslo objektams arba toliau naudojate if(HttpContext.Current!). =null) ir System.Web nuoroda (yuck), arba turite sugalvoti kažkokį statinio patvarumo teikėjo modelį, kurį reikia nustatyti prieš prisijungiant prie šių pavienių. Dvigubas pykinimas.

Sakykite, kad taip nėra.

Priedėlis: Visas žurnalas:


Tai yra pagrindinis PITA, nes, kiek aš matau, tai reiškia, kad vienintelis atkaklumo variantas 'ThreadStatic'esque elgesio ASP.Net yra naudoti HttpContext. Taigi savo verslo objektams, arba esate įstrigę su if(HttpContext.Current!=null) ir System.Web nuoroda (yuck) arba jūs turite sugalvoti tam tikrą teikėjo modelį savo statinis patvarumas, kurį reikės nustatyti prieš tašką, kai bet kuris iš šių singletons yra pasiekiamas. Dvigubas juokas.

Prašau kas nors pasakyti, kad taip nėra.

Appendix: That log in full:
[3748] INFORMACIJA 11:10:05,239 ASP. Global_asax. Application_BeginRequest() - BEGIN /ConcurrentRequestsDemo/SlowPage.aspx
[3748] INFORMACIJA 11:10:05,239 ASP. Global_asax. Application_BeginRequest() - threadid=, threadhash=, threadhash(now)=97, calldata=
[3748] INFORMACIJA 11:10:05,249 ASP. SlowPage_aspx.. ctor() - threadid=3748, threadhash=(cctor)97, threadhash(now)=97, calldata=3748, logicalcalldata=3748
[3748] INFORMACIJA 11:10:05,349 ASP. SlowPage_aspx. Page_Load() - threadid=3748, threadhash=(cctor)97, threadhash(now)=97, calldata=3748, logicalcalldata=3748
[3748] INFORMACIJA 11:10:05,349 ASP. SlowPage_aspx. Page_Load() - Lėtas puslapio miegas....

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

[3748] INFORMACIJA 11:10:06,350 ASP. SlowPage_aspx. Page_Load() - Lėtas puslapio pabudimas....
[3748] INFORMACIJA 11:10:06,350 ASP. SlowPage_aspx. Page_Load() - threadid=3748, threadhash=(cctor)97, threadhash(now)=97, calldata=3748, logicalcalldata=3748
[3748] INFORMACIJA 11:10:06,350 ASP. Global_asax. Application_EndRequest() - threadid=3748, threadhash=97, threadhash(now)=97, calldata=3748
[3748] INFORMACIJA 11:10:06,350 ASP. Global_asax. Application_EndRequest() - END /ConcurrentRequestsDemo/SlowPage.aspx

[4748] INFORMACIJA 11:10:06,791 ASP. FastPage_aspx. Page_Load() - threadid=2720, threadhash=(cctor)1835, threadhash(now)=1703, calldata=, logicalcalldata=, threadstatic=
[4748] INFORMACIJA 11:10:06,791 ASP. Global_asax. Application_EndRequest() - threadid=2720, threadhash=1835, threadhash(now)=1703, calldata=
[4748] INFORMACIJA 11:10:06,791 ASP. Global_asax. Application_EndRequest() - END /ConcurrentRequestsDemo/FastPage.aspx
Svarbiausia, kas atsitinka, kai suveikia "FastPage" Page_Load. ThreadID yra 4748, bet ThreadID I saugomas ctor's HttpContext yra 2720. Loginių gijų maišos kodas yra 1703, bet maišos kodas, kurį saugoju ctor, yra 1835. Trūksta visų duomenų, kuriuos saugojau CallContext (net ir duomenų, pažymėtų ILogicalThreadAffinnative), tačiau HttpContext vis dar yra. Kaip ir galima tikėtis, mano "ThreadStatic" taip pat nebėra.

Pagrindinis dalykas yra tai, kas atsitinka, kai "FastPage" Page_Load suveikia. ThreadID yra 4748, bet threadID, kurį saugojau HttpContext ctor, yra 2720. Loginės gijos maišos kodas yra 1703, bet tas, kurį išsaugojau ctor, yra 1835. Visi duomenys, kuriuos saugojau CallContext, dingo (net ir pažymėti ILogicalThreadAffinative), bet HttpContext vis dar yra. Kaip ir tikėjotės, mano "ThreadStatic" taip pat nebėra.
(Pabaiga)




Ankstesnis:.NET/C# rinkinys Any() arba Count(), kuris yra greitesnis
Kitą:Kaip tingus C# saugo siūlus
 Savininkas| Paskelbta 2023-06-30 20:35:23 |
 Savininkas| Paskelbta 2023-07-02 09:59:06 |
Skambučio kontekstas

Vardų sritis: System.Runtime.Remoting.Messaging
Pilnas pavadinimas: System.Runtime.Remoting.Messaging.CallContext

Tikslas: pateikti atributų rinkinį, kuris perduodamas kartu su vykdymo kodo keliu, paprasčiau tariant: suteikti galimybę perduoti duomenis gijų (kelių gijų / vienos gijos) kodo vykdymo kelyje.
metodasapibūdinimasAr jį galima naudoti kelių gijų aplinkoje
Nustatyti duomenisSaugoti nurodytą objektą ir susieti jį su nurodytu pavadinimu.ne
Gauti duomenisNuskaityti objektą nurodytu pavadinimu iš System.Runtime.Remoting.Messaging.CallContextne
Loginis rinkinysSaugokite nurodytą objektą loginio iškvietimo kontekste ir susiekite jį su nurodytu pavadinimu.būti
LogicalGetDataNuskaityti objektus nurodytais pavadinimais iš loginio iškvietimo konteksto.būti
FreeNamedDataSlotIštuštinkite duomenų lizdus nurodytu pavadinimu.būti
Pagrindinio kompiuterio kontekstasGaukite arba nustatykite pagrindinio kompiuterio kontekstą, susietą su dabartine gija. Žiniatinklio aplinkoje jis lygus System.Web.HttpContext.Currentne


Atsakomybės apribojimas:
Visa programinė įranga, programavimo medžiaga ar straipsniai, kuriuos skelbia Code Farmer Network, yra skirti tik mokymosi ir mokslinių tyrimų tikslams; Aukščiau nurodytas turinys negali būti naudojamas komerciniais ar neteisėtais tikslais, priešingu atveju vartotojai prisiima visas pasekmes. Šioje svetainėje pateikiama informacija gaunama iš interneto, o ginčai dėl autorių teisių neturi nieko bendra su šia svetaine. Turite visiškai ištrinti aukščiau pateiktą turinį iš savo kompiuterio per 24 valandas nuo atsisiuntimo. Jei jums patinka programa, palaikykite autentišką programinę įrangą, įsigykite registraciją ir gaukite geresnes autentiškas paslaugas. Jei yra kokių nors pažeidimų, susisiekite su mumis el. paštu.

Mail To:help@itsvse.com