Sākotnējā:Hipersaites pieteikšanās ir redzama.
Kopsavilkuma: Pat ja domājat, ka zināt, ko darāt, nav droši glabāt kaut ko ThreadStatic elementā, CallContext vai Thread lokālajā krātuvē ASP.Net lietojumprogrammā, ja vērtība var būt iestatīta iepriekš. Page_Load (piemēram, IHttpModule vai lapas konstruktorā), bet piekļuves laikā vai pēc tās.
[Atjauninājums: 2008. gada augusts Ņemot vērā, ka diezgan daudzi cilvēki turpina saites uz šo rakstu, es uzskatu, ka ir nepieciešams precizēt, ka šī pavedienu maiņas uzvedība notiek ļoti konkrētā lapas dzīves cikla brīdī, nevis tad, kad tas šķiet.] Mans formulējums pēc Jeff Newsom citēšanas ir neveiksmīgs. Izņemot to, es esmu ļoti priecīgs (un glaimojošs) redzēt atsauces uz šo rakstu vairākas reizes dizaina diskusijās par pareizu HttpContext apstrādi. Es priecājos, ka cilvēkiem tas ir noderīgi. ]
Ir daudz neskaidrību par to, kā ieviest lietotājam specifiskus singletonus ASP.Net - tas ir, globālie dati ir globāli tikai vienam lietotājam vai pieprasījumam. Tā nav nekas neparasts: publicēt transakciju, drošības konteksta vai citus "globālus" datus vienuviet, nevis ievietot tos katrā metodes izsaukumā, jo smilšmāla dati ļauj skaidrāk (un lasāmāk) ieviest. Tomēr, ja neesat uzmanīgs, šī ir lieliska vieta, kur nošaut sev pēdā (vai galvā). Es domāju, ka zinu, kas notiek, bet es to nezināju.
Vēlamais variants būs vientuļšSaglabāts HttpContext.Current.Items, vienkāršs un drošs, bet attiecīgo singltonu saistot ar tā izmantošanu ASP.Net lietojumprogrammā. Ja vientuļais neizdodas jūsu biznesa objektā, tad tas nav ideāls. Pat ja īpašuma piekļuve tiek ietīta if priekšrakstā
Summary: Pat ja jūs domājat, ka zināt, ko darāt, nav droši kaut ko glabāt ThreadStatic dalībniekā, CallContext vai Thread lokālajā krātuvē ASP.Net lietojumprogrammā, ja ir iespēja ka vērtība var būt iestatīta pirms Page_Load (piemēram, IHttpModule vai lapas konstruktorā), bet tai var piekļūt laikā vai pēc tam.
[Atjauninājums: 2008. gada augusts Ņemot vērā diezgan lielo cilvēku skaitu, kas turpina saites uz šo ziņu, es jūtu nepieciešamību precizēt, ka šī pavedienu maiņas uzvedība notiek ļoti konkrētā lapas punktā dzīves cikls, nevis tad, kad tas šķiet. Mans formulējums pēc Jef Newson citāta bija neveiksmīgs. Neskatoties uz to, es esmu bijis ārkārtīgi gandarīts (un glaimojis) par to, cik reižu esmu redzējis šo ziņu citēts dizaina diskusijās par atbilstošu rīcību ar HttpContext. Es priecājos, ka cilvēkiem tas bija noderīgs.]
Ir daudz neskaidrību par to, kā ieviest lietotājam specifiskus singletonus ASP.Net - tas ir, globālie dati, kas ir globāli tikai vienam lietotājam vai pieprasījumam. Tā nav nekas neparasts: publicēt darījumus, drošības kontekstu vai citus "globālus" datus vienuviet, nevis virzīt tos cauri katram metodes izsaukumam, kā tramp dati var radīt tīrāka (un lasāmāka) ieviešana. Tomēr tā ir lieliska vieta, kur nošaut sev pēdā (vai galvā), ja neesat uzmanīgs. Es domāju, ka zinu, kas notiek, bet es to nezināju.
Vēlamā opcija, glabājot vientuļniekus HttpContext.Current.Items, ir vienkārša un droša, bet saista attiecīgo singletonu ar izmantošanu ASP.Net lietojumprogrammā. Ja vientuļnieks ir uz leju jūsu biznesa objektos, tas nav ideāli. Pat ja īpašuma piekļuvi iesaiņojat if priekšrakstā
... tad jums joprojām ir jāatsaucas uz System.Web no šīs asamblejas, kas mēdz iekļaut vairāk "webby" objektu nepareizā vietā.
Alternatīvas ir izmantot [ThreadStatic] statisko elementu, Thread vietējo krātuvi (kas gandrīz ir tas pats) vai CallContext.
Problēmas ar [ThreadStatic] ir labi dokumentētas, bet, apkopojot: Lauka iniciatori šauj tikai uz pirmo pavedienu ThreadStatic datiem ir nepieciešama skaidra tīrīšana (piemēram, EndRequest), jo, kamēr pavediens ir sasniedzams, ThreadStatic dati netiks GC, tāpēc jūs varētu nopludināt resursus. ThreadStatic dati ir tikai jebkurš labs pieprasījumā, jo nākamais pieprasījums var ienākt citā pavedienā un iegūt kāda cita datus. Skots Hanselmans pareizi saprot, ka ThreadStatic nespēlē labi ar ASP.Net, bet pilnībā neizskaidro, kāpēc.
Krātuve CallContext mazina dažas no šīm problēmām, jo konteksts izzūd pieprasījuma beigās un GC galu galā radīsies (lai gan jūs joprojām varat nopludināt resursus, līdz GC notiek, ja jūs glabājat vienreizlietojamās preces). Turklāt CallContext ir veids, kā HttpContext tiek glabāts, tāpēc tam jābūt ok, vai ne?. Neatkarīgi no tā, jūs domājat (kā es to darīju), ka ar nosacījumu, ka jūs iztīrīsiet pēc sevis katra pieprasījuma beigās, viss būtu labi: "Ja pieprasījuma sākumā inicializējat mainīgo ThreadStatic un pieprasījuma beigās pareizi apstrādājat atsauces objektu, es riskētu apgalvot, ka nekas slikts nenotiks
"Tagad,Es varētu kļūdīties. CLR var pārtraukt pavediena mitināšanu pusceļā, kaut kur serializēt tā kaudzi, piešķirt tai jaunu kaudzi un ļaut tai sākt izpildīt。 Es par to ļoti šaubos. Es domāju, ka ir iedomājams, ka hiperpavediens arī padarītu lietas sarežģītas, bet es arī šaubos. ”
Džefs Ņūsoms
"Ja pieprasījuma sākumā inicializējat mainīgo ThreadStatic un pieprasījuma beigās pareizi atbrīvojaties no atsauces objekta, es izeju uz ekstremitāti un apgalvošu, ka nekas Slikti notiks. Jūs pat esat forši starp kontekstiem vienā un tajā pašā AppDomain
"Tagad es varētu kļūdīties šajā jautājumā. Clr potenciāli varētu apturēt pārvaldīto pavedienu straumes vidū, kaut kur seriālizēt tā kaudzi, piešķirt tai jaunu kaudzi un ļaut tai sākt izpildīt. Es par to nopietni šaubos. Es domāju, ka ir iedomājams, ka hiperpavediens arī apgrūtina lietas, bet es arī šaubos, ka tas. Džefs Ņūsoms Atjauninājums: Tas ir maldinoši. Vēlāk paskaidrošu, ka šī pavediena maiņa var notikt tikai starp BeginRequest un Page_Load, bet Džefa atsauce rada ļoti spēcīgu attēlu, kuru man neizdevās uzreiz izlabot.
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. Tātad, kādā brīdī ASP.NET nolemt, ka ir pārāk daudz I/O pavedienu, kas apstrādā citus pieprasījumus. […] Tas pieņem pieprasījumus un ievieto tos rindā tikai iekšējā rindas objektā ASP.NET izpildlaikā. Pēc tam pēc rindas I/O pavediens pieprasīs darba pavedienu, un pēc tam I/O pavediens atgriezīsies savā baseinā. […] Tāpēc ASP.NET ļausim šim darbiniekam apstrādāt pieprasījumu. Tas nodrošinās to ASP.NET izpildlaiku, tāpat kā I/O pavediens pie zemas slodzes.
Tātad kādā brīdī ASP.NET nolemj, ka ir pārāk daudz I/O pavedienu, kas apstrādā citus pieprasījumus. [...] Tas vienkārši ņem pieprasījumu un ievieto to rindā šajā iekšējā rindas objektā ASP.NET izpildlaikā. Pēc tam, kad tas ir sakārtots rindā, I/O pavediens lūgs darba pavedienu, un pēc tam I/O pavediens tiks atgriezts savā baseinā. [...] Tātad ASP.NET darbiniekam pavediens apstrādās pieprasījumu. Tas to aizvedīs ASP.NET izpildlaikā, tāpat kā I/O pavedienam būtu zema slodze. Tagad es vienmēr par to zināju, bet es domāju, ka tas notika ļoti agri un man bija vienalga. Tomēr šķiet, ka es kļūdos. Mums bija problēma ASP.Net lietotnē, kad lietotājs noklikšķināja uz saites pēc noklikšķināšanas uz citas saites, un mūsu lietotnei bija nulles atsauces izņēmums vienā no singletoniem (es izmantoju CallContext, nevis ThreadStatic singleton, bet tas izrādījās nebūtisks).
Es veicu dažus pētījumus par to, kā tieši ASP.Net pavedieni darbojas, un saņēmu pretrunīgus viedokļus, kas maskēti kā fakti (pieprasījumi ir pavedieni pieprasījumos, bet pieprasījumi ir noenkuroti uz pavedieniem to dzīves laikā), tāpēc es nokopēju savu problēmu testa lietojumprogrammā ar lēnu lapu (gulēt uz sekundi) un ātru lapu. Es noklikšķinu uz saites uz lēno lapu, un pirms lapas atgriešanās es noklikšķinu uz saites uz ātro lapu. Rezultāts (log4net izgāztuve par notiekošo) pārsteidza manu prātu.
Izvade parāda, ka otrajam pieprasījumam BeginRequest notikums un lapas konstruktors HttpModule konveijera iedarbina vienu pavedienu, bet page_Load citā. Otrais pavediens jau ir migrējis HttpContext no pirmā pavediena, bet ne CallContext vai ThreadStatic (piezīme: tā kā pats HttpContext tiek glabāts CallContext, tas nozīmē, ka ASP.Net skaidri migrē HttpContext). Teiksim to vēlreiz:
Tagad es vienmēr zināju par to, bet es pieņēmu, ka tas notika pietiekami agri procesā, ka man bija vienalga. Tomēr šķiet, ka es kļūdījos. Mums ir radusies problēma mūsu ASP.Net lietotnē, kad lietotājs noklikšķina uz vienas saites uzreiz pēc noklikšķināšanas uz citas, un mūsu lietotne uzspridzinās ar nulles atsauces izņēmumu vienam no mūsu vientuļajiem (es izmantoju CallContext nav ThreadStatic vientuļajam, bet izrādās, ka tam nav nozīmes).
Es veicu nedaudz pētījumu par to, kā tieši ASP. Net pavedieni darbojas, un ieguva pretrunīgus viedokļus, kas maskējas kā fakts (pieprasījumi ir pavediena veikli pieprasījumā pret pieprasījumiem ir piesprausti pavedienam uz visu mūžu), tāpēc es atkārtoju savu Problēma testa lietojumprogrammā ar lēnu lapu (miega sekundi) un ātru lapu. Es noklikšķinu uz lēnās lapas saites un pirms lapas atgriešanās es noklikšķinu uz ātrās lapas saites. Rezultāti (log4net izgāztuve par notiekošo) mani pārsteidza.
Izvade parāda, ka otrajam pieprasījumam BeginRequest notikumi HttpModule konveijerā un lapas konstruktors ieslēdzas vienā pavedienā, bet Page_Load tiek aktivizēts citā. Otrajā pavedienā HttpContext ir migrēts no pirmā, bet ne CallContext vai ThreadStatic (NB: tā kā HttpContext pats tiek glabāts CallContext, tas nozīmē, ka ASP.Net ir skaidri migrējot HttpContext pāri). Vienkārši izskaidrosim to vēlreiz:
- Pavedienu pārslēgšana notiek pēc IHttpHandler izveides
- Pēc lapas lauka inicializatora un konstruktora palaišanas
- Pēc jebkuriem BeginRequest, AuthenticateRequest, AquireSessionState tipa notikumiem, ko izmanto Global.ASA/IHttpModules.
- Tikai HttpContext tiek migrēts uz jauno pavedienu
Pavedienu slēdzis notiek pēc IHttpHandler izveides Pēc lapas lauku inicializatoru un konstruktora palaišanas Pēc jebkuriem BeginRequest, AuthenticateRequest, AquireSessionState tipa notikumiem, kurus izmanto jūsu Global.ASA / IHttpModules. Tikai HttpContext migrē uz jauno pavedienu Tas ir liels PITA, jo no tā, ko esmu redzējis, tas nozīmē, ka vienīgā noturības iespēja "ThreadStatic" klases uzvedībai ASP.Net ir izmantot HttpContext. Tātad saviem biznesa objektiem vai nu turpināt izmantot if(HttpContext.Current!). =null) un System.Web atsauce (yuck), vai arī jums ir jānāk klajā ar kādu statiskās noturības pakalpojumu sniedzēja modeli, kas jāiestata pirms piekļuves šiem singletoniem. Dubultā slikta dūša.
Lūdzu, sakiet, ka tas tā nav.
Papildinājums: Pilns žurnāls:
Tas ir liels PITA, jo, cik es redzu, tas nozīmē, ka vienīgā noturības iespēja 'ThreadStatic'esque uzvedībai ASP.Net ir izmantot HttpContext. Tātad jūsu biznesa objektiem vai nu jūs esat iestrēdzis ar if(HttpContext.Current!=null) un System.Web atsauci (yuck), vai arī jums ir jāizdomā kāds pakalpojumu sniedzēja modelis jūsu statiskā noturība, kas būs jāiestata pirms brīža, kad kāds no šiem singletoniem ir pieejams. Divkāršs juks.
Lūdzu, kāds saka, ka tā nav.
Appendix: That log in full: [3748] INFORMĀCIJA 11:10:05,239 ASP. Global_asax. Application_BeginRequest() - BEGIN /ConcurrentRequestsDemo/SlowPage.aspx [3748] INFORMĀCIJA 11:10:05,239 ASP. Global_asax. Application_BeginRequest() - threadid=, threadhash=, threadhash(now)=97, calldata= [3748] INFORMĀCIJA 11:10:05,249 ASP. SlowPage_aspx.. ctor() - threadid=3748, threadhash=(cctor)97, threadhash(tagad)=97, calldata=3748, logicalcalldata=3748 [3748] INFORMĀCIJA 11:10:05,349 ASP. SlowPage_aspx. Page_Load() - threadid=3748, threadhash=(cctor)97, threadhash(now)=97, calldata=3748, logicalcalldata=3748 [3748] INFORMĀCIJA 11:10:05,349 ASP. SlowPage_aspx. Page_Load() - Lēns lapas miegs....
[2720] INFORMĀCIJA 11:10:05,669 ASP. Global_asax. Application_BeginRequest() - BEGIN /ConcurrentRequestsDemo/FastPage.aspx [2720] INFORMĀCIJA 11:10:05,679 ASP. Global_asax. Application_BeginRequest() - threadid=, threadhash=, threadhash(tagad)=1835, calldata= [2720] INFORMĀCIJA 11:10:05,679 ASP. FastPage_aspx .. ctor() - threadid=2720, threadhash=(cctor)1835, threadhash(tagad)=1835, calldata=2720, logicalcalldata=2720, threadstatic=2720
[3748] INFORMĀCIJA 11:10:06,350 ASP. SlowPage_aspx. Page_Load() - Lēna lapas pamošanās.... [3748] INFORMĀCIJA 11:10:06,350 ASP. SlowPage_aspx. Page_Load() - threadid=3748, threadhash=(cctor)97, threadhash(now)=97, calldata=3748, logicalcalldata=3748 [3748] INFORMĀCIJA 11:10:06,350 ASP. Global_asax. Application_EndRequest() - threadid=3748, threadhash=97, threadhash(now)=97, calldata=3748 [3748] INFORMĀCIJA 11:10:06,350 ASP. Global_asax. Application_EndRequest() - END /ConcurrentRequestsDemo/SlowPage.aspx
[4748] INFORMĀCIJA 11:10:06,791 ASP. FastPage_aspx. Page_Load() - threadid=2720, threadhash=(cctor)1835, threadhash(now)=1703, calldata=, logicalcalldata=, threadstatic= [4748] INFORMĀCIJA 11:10:06,791 ASP. Global_asax. Application_EndRequest() - threadid=2720, threadhash=1835, threadhash(tagad)=1703, calldata= [4748] INFORMĀCIJA 11:10:06,791 ASP. Global_asax. Application_EndRequest() - END /ConcurrentRequestsDemo/FastPage.aspx Galvenais ir tas, kas notiek, kad tiek aktivizēta FastPage Page_Load. ThreadID ir 4748, bet ThreadID, ko es saglabāju ctor's HttpContext, ir 2720. Loģisko pavedienu jaucējkods ir 1703, bet jaucējkods, ko es glabāju ctor, ir 1835. Trūkst visu CallContext glabāto datu (pat dati, kas apzīmēti ar ILogicalThreadAffinnative), bet HttpContext joprojām ir. Kā jūs varētu sagaidīt, arī mans ThreadStatic ir pazudis.
Galvenais ir tas, kas notiek, kad FastPage Page_Load tiek atlaists. ThreadID ir 4748, bet pavediena ID, ko es saglabāju HttpContext ctor, ir 2720. Loģiskā pavediena jaucējkods ir 1703, bet tas, ko es saglabāju ctorā, ir 1835. Visi dati, ko es glabāju CallContext, ir pazuduši (pat tie, kas atzīmēti ar ILogicalThreadAffinative), bet HttpContext joprojām ir. Kā jūs varētu sagaidīt, arī mans ThreadStatic ir pazudis. (Beigas)
|