1: Vispārīga informācija
1. Pastāstiet stāstus
Pirms dažām dienām, kad es indeksēju rakstu par .NET Advanced Debugging uz github, es atradu interesantu komentāru, skatiet rakstu, lai iegūtu sīkāku informāciju, ekrānuzņēmums ir šāds:
Tas droši vien nozīmē, ka Task.Result izpilde zem Winform galvenā pavediena radīs strupceļu, es arī paskatījos uz atsauces saiti attēlā, Stefans ir absolūtais priekšnieks, bet šis raksts galvenokārt ir par lielas teksta rindkopas indoktrināciju, un tas īsti neļauj jums redzēt to, ko redzat, tāpēc es to analizēšu no windbg perspektīvas.
2: Windbg analīze
1. Vai tas tiešām būs strupceļā?
Protams, es neesmu spēlējis winform daudzus gadus, un es nevaru saprast, vai tas būs, vismaz ne konsolē.
Kods ir ļoti vienkāršs, palaidiet programmu, noklikšķiniet uz klikšķi, un, protams, interfeiss ir iestrēdzis, kas ir mazliet neticami.
2. Meklējiet strupceļa cēloni
Pēc tam pasteidzieties un pievienojiet vējš procesam, lai uzzinātu.
1) Apskatiet galveno pavedienu Interfeiss nereaģē, tāpēc, protams, galvenais pavediens ir iestrēdzis, tāpēc jums ir jāapskata, ko galvenais pavediens dara šobrīd. Izmantojiet komandu ~0s + !clrstack.
0:000> !clrstack OS pavediena ID: 0x5a10 (0) Bērnu SP IP zvanu vietne 0000004d10dfde00 00007ffb889a10e4 [GCFrame: 0000004d10dfde00] 0000004d10dfdf28 00007ffb889a10e4 [HelperMethodFrame_1OBJ: 0000004d10dfdf28] System.Threading.Monitor.ObjWait(Būls, Int32, System.Object) 0000004d10dfe040 00007ffb66920d64 System.Threading.ManualResetEventSlim.Wait(Int32, System.Threading.CancellationToken) 0000004d10dfe0d0 00007ffb6691b4bb System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken) 0000004d10dfe140 00007ffb672601d1 System.Threading.Tasks.Task.InternalWait(Int32, System.Threading.CancellationToken) 0000004d10dfe210 00007ffb6725cfa7 System.Threading.Tasks.Task'1[[System.__Canon, mscorlib]]. GetResultCore(Būla) 0000004d10dfe250 00007ffb18172a1b WindowsFormsApp4.Form1.button1_Click(System.Object, System.EventArgs) [E:\net5\ConsoleApp1\WindowsFormsApp4\Form1.cs @ 26] 0000004d10dfe2b0 00007ffb3a024747 System.Windows.Forms.Control.OnClick(System.EventArgs) 0000004d10dfe2f0 00007ffb3a027b83 System.Windows.Forms.Button.OnClick(System.EventArgs) 0000004d10dfe340 00007ffb3a837231 System.Windows.Forms.Button.OnMouseUp(System.Windows.Forms.MouseEventArgs) 0000004d10dfe400 00007ffb3a7e097d System.Windows.Forms.Control.WmMouseUp(System.Windows.Forms.Message ByRef, System.Windows.Forms.MouseButtons, Int32) 0000004d10dfe480 00007ffb3a0311cc System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef) 0000004d10dfe540 00007ffb3a0b0c97 System.Windows.Forms.ButtonBase.WndProc(System.Windows.Forms.Message ByRef) 0000004d10dfe5c0 00007ffb3a0b0be5 System.Windows.Forms.Button.WndProc(System.Windows.Forms.Message ByRef) 0000004d10dfe5f0 00007ffb3a030082 System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr) 0000004d10dfe690 00007ffb3a765a02 DomainBoundILStubClass.IL_STUB_ReversePInvoke(Int64, Int32, Int64, Int64) 0000004d10dfe9d0 00007ffb776d221e [InlinedCallFrame: 0000004d10dfe9d0] System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG ByRef) 0000004d10dfe9d0 00007ffb3a0b9489 [InlinedCallFrame: 0000004d10dfe9d0] System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG ByRef) 0000004d10dfe9a0 00007ffb3a0b9489 DomainBoundILStubClass.IL_STUB_PInvoke(MSG ByRef) 0000004d10dfea60 00007ffb3a046661 System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop( IntPtr, Int32, Int32) 0000004d10dfeb50 00007ffb3a045fc7 System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext) 0000004d10dfebf0 00007ffb3a045dc2 System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext) 0000004d10dfec50 00007ffb181708e2 WindowsFormsApp4.Program.Main() [E:\net5\ConsoleApp1\WindowsFormsApp4\Program.cs @ 19] 0000004d10dfee78 00007ffb776d6923 [GCFrame: 0000004d10dfee78] No steka izejas galvenais pavediens beidzot ir iestrēdzis Monitor.ObjWait zem Task.Result, kas nozīmē, ka tas vēl nav ieguvis pēdējo jsonString, kas ir ļoti dīvaini, ir pagājušas vairākas minūtes, vai ir problēma ar tīklu? Mans tīkls ir 100 m pilns ar ugunsspēku...
2) Kur gāja jsonString? Ja pārvaldītajā kaudzē tiek atrasta jsonString, tas nozīmē, ka programmā ir kaut kas, kas aizkavē rezultātu, izmantojiet komandu !dumpheap -type String -min 8500 + !do 000001f19002fcf0, lai apskatītu, kā parādīts zemāk redzamajā attēlā:
No attēla var skaidri redzēt, ka html ir atgriezies, jo tas viss ir atpakaļ, kāpēc Task.Result vēl nav beidzies? Nākamais solis ir redzēt, kam pieder šis html, un izmantot !gcroot.
0:000> !gcroot 000001f19002fcf0 5a10 pavediens: 0000004d10dfe250 00007ffb18172a1b WindowsFormsApp4.Form1.button1_Click(System.Object, System.EventArgs) [E:\net5\ConsoleApp1\WindowsFormsApp4\Form1.cs @ 26] RBP+10: 0000004D10Dfe2B0 -> 000001f180007f78 WindowsFormsApp4.Form1 -> 000001f180070d68 System.ComponentModel.EventHandlerList -> 000001f180071718 System.ComponentModel.EventHandlerList+ListEntry -> 000001f1800716d8 System.EventHandler -> 000001f1800716b0 System.Windows.Forms.ApplicationContext -> 000001f180071780 System.EventHandler -> 000001f18006ab38 System.Windows.Forms.Application+ThreadContext -> 000001f18006b140 System.Windows.Forms.Application+MarshalingControl -> 000001f18016c9c8 System.Collections.Queue -> 000001f18016ca00 System.Object[] -> 000001f18016c948 System.Windows.Forms.Control+ThreadMethodEntry -> 000001f18016c8b8 System.Object[] -> 000001f1800e6f80 System.Action -> 000001f1800e6f60 System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner -> 000001f1800a77d0 WindowsFormsApp4.Form1+<GetJsonAsync>d__2 -> 000001f1800b4e50 System.Threading.Tasks.Task'1[[System.String, mscorlib]] -> 000001f19002fcf0 System.String
Atrastas 1 unikālas saknes (palaist '! GCRoot -all", lai redzētu visas saknes). No izvades rezultātiem šo System.String beidzot tur WindowsFormsApp4.Form1 no 5a10 pavediena, un jūs varat izmantot !t, lai pārbaudītu, kas ir pavediens 5a10.
0:000> !t Bloķēt ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt izņēmums 0 1 5a10 000001f1f1b01200 2026020 Preventīvais 000001F1800E70E8:000001F1800E7FD0 000001f1f1ad5b90 0 STA 2 2 712c 000001f1f1b2a270 2b220 Preventīvais 000000000000000000:000000000000000000 0000001f1f1ad5b90 0 MTA (fināls) Es eju, 5a10 izrādījās galvenais pavediens, tas patiešām bija nedaudz mulsinoši, galvenais pavediens bija iestrēdzis, un stīgu turēja galvenais pavediens, pilnīgi neizskaidrojami.
3) Meklējiet izlaušanās punktus Joprojām atgriežoties un mierīgi domājot par šo atsauces ķēdi, es atklāju, ka šeit ir rinda: -> 000001f18016c9c8 System.Collections.Queue, man ir ideja, es varu atkļūdot avota kodu, ievietojot pārtraukuma punktu rindā, un rīks izmanto DnSpy, vienkārši dariet to.
Kā redzat no attēla, ievadot rindu, tiek izmantots 10. pavediens, kas nozīmē, ka virkne šobrīd vēl nav galvenā pavediena.
No diagrammas var redzēt, ka nepārtraukto uzdevumu beidzot ieplāno WindowsFormsSynchronizationContext.Post kontrolētajā rindā, un dati šajā rindā ir jāizpilda lietotāja interfeisa pavedienam, tāpēc ir šāds dialogs:
Galvenais pavediens: Uzdevums brāli, kad jūs to pabeigsiet? Es gaidu, kad jūs pabeigsiet signālu?
uzdevums:Brāli, ja tu mani neizpildīsi, kā es varu to pabeigt?
Galvenais pavediens:Ak...
Rezumējot: turpinājuma uzdevums ir sasniedzis rindu, gaidot galvenā pavediena izpildi, un šobrīd galvenais pavediens ir apdullināts un gaida, kad turpinājuma uzdevums tiks pabeigts=true, problēma ir šeit: kā turpinājuma uzdevumu nevar izpildīt Complete=true? Tātad viņi ir šādi.
Trešais: Kā to uzlauzt
Zinot cēloni un sekas, šī plaisāšanas metode ir vienkārša, aptuveni sadalīta divos veidos.
1. Aizliegts nomest turpinājuma uzdevumu rindā
Lai nogrieztu šo ceļu, tas nozīmē, ka pavedienu pūlam ir jābeidz uzdevums pats, lai lietotāja interfeisa pavediens varētu uztvert, ka uzdevums ir pabeigts, un visbeidzot lietotāja interfeisa pavediens var iegūt galīgo html, kas ir pievienot ConfigureAwait(false) pēc gaidīšanas, šādi:
2. Bloķējiet galveno pavedienu
Ja galvenais pavediens nav bloķēts, tad galvenais pavediens var brīvi iegūt uzdevumus, kas jāizpilda Control.Queue, un metode ir ļoti vienkārša, vienkārši pievienojiet gaidīt pirms GetJsonAsync.
Trīs: kopsavilkums
Secinājums ir darīt vairāk savas praktiskās prakses, teorētiskās zināšanas tevī piespiedu kārtā ieaudzina citi, neatkarīgi no tā, vai tas ir pareizi vai nepareizi, patiesībā tev sirdī nav dibena, praktiska pārbaude ir tas, kas tev patiešām pieder, un ir grūti aizmirst, galu galā tu esi patiešām pieredzējis, praktizējis un pārbaudījis.
Sākotnējā:Hipersaites pieteikšanās ir redzama.
|