1: Ozadje
1. Pripovedovati zgodbe
Pred nekaj dnevi, ko sem indeksiral članek o .NET Advanced Debuggging na github, sem našel zanimiv komentar, za podrobnosti glejte članek, posnetek zaslona je naslednji:
Verjetno to pomeni, da bo izvajanje Task.Result pod glavno nitjo Winforma povzročilo zastoj, pogledal sem tudi referenčno povezavo na sliki, Stephen je absolutni šef, vendar je ta članek predvsem o indoktrinaciji velikega odstavka besedila in ti ne dovoli videti, kar vidiš, zato bom analiziral z vidika windbg.
2: Analiza Windbg
1. Ali bo res mrtva točka?
Seveda že vrsto let nisem igral Winforma in ne morem ugotoviti, ali bo, vsaj ne na konzoli.
Koda je zelo preprosta, zaženeš program, klikneš klik in res, vmesnik se zatakne, kar je kar neverjetno.
2. Poiščite vzrok zastoja
Nato pohitite in priložite windbg na postopek, da izveste.
1) Poglej glavno nit Vmesnik ni odziven, zato je glavna nit naravno zataknjena, zato morate pogledati, kaj glavna nit počne v tem trenutku. Uporabi ukaz ~0s + !clrstack.
0:000> !clrstack ID OS niti: 0x5a10 (0) Child SP IP klicna stran 0000004d10dfde00 00007ffb889a10e4 [GCFrame: 0000004d10dfde00] 0000004d10dfdf28 00007ffb889a10e4 [HelperMethodFrame_1OBJ: 0000004d10dfdf28] System.Threading.Monitor.ObjWait(Boolean, 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(Boolean) 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] Iz izhoda sklada se glavna nit končno zatakne na Monitor.ObjWait pod Task.Result, kar pomeni, da še ni pridobil zadnjega jsonString, kar je zelo nenavadno, minilo je že nekaj minut, ali je težava z omrežjem? Moja mreža je 100M polna ognjene moči...
2) Kam je izginil jsonString? Če je jsonString najden na upravljani kopici, to pomeni, da je v programu nekaj, kar povzroča zakasnitev rezultata, uporabite ukaz !dumpheap -type String -min 8500 + !do 000001f19002fcf0 za ogled, kot je prikazano na spodnji sliki:
Na sliki je jasno razvidno, da se je html vrnil, ker je vse nazaj, zakaj Task.Result še ni končan? Naslednji korak je preveriti, kdo ima ta html in uporabiti !gcroot.
0:000> !gcroot 000001f19002fcf0 Tema 5a10: 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 Sistem. Cilj[] -> 000001f18016c948 System.Windows.Forms.Control+ThreadMethodEntry -> 000001f18016c8b8 System.Object[] -> 000001f1800e6f80 Sistem. Akcija -> 000001f1800e6f60 System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner -> 000001f1800a77d0 WindowsFormsApp4.Form1+<GetJsonAsync>d__2 -> 000001f1800b4e50 System.Threading.Tasks.Task'1[[System.String, mscorlib]] -> 000001f19002fcf0 System.String
Našel sem 1 edinstveno korenino (zaženi '! GCRoot -all' za ogled vseh korenin). Iz izhodnih rezultatov je ta System.String končno shranjen v WindowsFormsApp4.Form1 iz niti 5a10, in lahko uporabite !t za preverjanje, kaj je nit 5a10.
0:000> !t Zakleni ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 0 1 5a10 000001f1f1b01200 2026020 Preemptive 000001F1800E70E8:000001F1800E7FD0 000001f1f1ad5b90 0 STA 2 2 712c 0000001f1f1b2a270 2b220 Preemptivni 00000000000000000:000000000000000000000000 0000001f1f1ad5b90 0 MTA (Finalizer) Rekel sem, 5a10 se je izkazalo za glavno nit, bilo je res nekoliko zmedeno, glavna nit je bila zataknjena, nit pa je držala glavna nit, popolnoma nepojasnjeno.
3) Poiščite točke preboja Še vedno sem mirno razmišljal o tej referenčni verigi in ugotovil, da je tukaj čakalna vrsta: -> 000001f18016c9c8 System.Collections.Queue, imam idejo, lahko razhroščim izvorno kodo tako, da v vrsto postavim prekinitev, orodje uporablja DnSpy, samo naredi to.
Kot lahko vidite na sliki, se ob vstopu v Queue uporablja nit 10, kar pomeni, da niza v tem trenutku še ni zadržana v glavni niti.
Iz diagrama je razvidno, da je neprekinjena naloga končno razporejena tako, da WindowsFormsSynchronizationContext.Post vključi v vrsto pod nadzorom, podatki v tej vrsti pa morajo biti izvedeni s strani UI niti, zato je naslednji dialog:
Glavna nit: Naloga, brat, kdaj jo boš končal? Čakam, da dokončaš signal?
opravilo:Brat, če me ne usmrtiš, kako naj končam?
Glavna nit:Oh...
Povzetek: naloga nadaljevanja je dosegla vrsto, ki čaka, da se glavna nit izvede, in v tem trenutku je glavna nit omamljena ter čaka, da se naloga nadaljevanja zaključi =true, težava je tukaj: kako naloga nadaljevanja ne bi bila izvedena Complete=true? Torej so takšni.
Tretjič: Kako ga razvozlati
Ob poznavanju vzroka in posledice je ta metoda razpokanja preprosta, približno razdeljena na dve vrsti.
1. Prepovedano je vključiti nadaljevalno nalogo v čakalno vrsto
Za prekinitev te poti je posledica, da se nitni bazen sam zaključi nalogo, tako da UI nit zazna, da je naloga zaključena, nazadnje pa UI nit dobi končni html, ki je dodajanje ConfigureAwait(false) po čakanju, kot sledi:
2. Blokiraj glavno nit
Če glavna nit ni blokirana, lahko glavna nit prosto pridobi naloge, ki jih je treba izvesti v Control.Queue, metoda pa je zelo preprosta, le dodaj await pred GetJsonAsync.
Tretjič: Povzetek
Zaključek je, da boš več samostojno prakticiral, teoretično znanje ti drugi prisilno vcepijo, ne glede na to, ali je prav ali narobe, pravzaprav nimaš dna v srcu, praktična preveritev je tisto, kar ti resnično pripada, in težko je pozabiti, saj si res izkusil, vadil in preverjal.
Izvirno:Prijava do hiperpovezave je vidna.
|