1: Achtergrond
1. Verhalen vertellen
Een paar dagen geleden, toen ik het artikel over .NET Advanced Debugging naar github indexeerde, vond ik een interessante opmerking, zie het artikel voor details, de screenshot is als volgt:
Het betekent waarschijnlijk dat het uitvoeren van Task.Result onder de hoofdthread van Winform een deadlock veroorzaakt, ik heb ook naar de referentielink op de foto gekeken, Stephen is de absolute baas, maar dit artikel gaat vooral over de indoctrinatie van een grote alinea tekst, en het laat je eigenlijk niet zien wat je ziet, dus ik zal het analyseren vanuit het perspectief van windbg.
2: Windbg-analyse
1. Zal het echt vastlopen?
Natuurlijk heb ik al jaren geen Winform meer gespeeld, en ik weet niet of het zal gebeuren, althans niet op de console.
De code is heel simpel, start het programma, klik klik, en inderdaad, de interface zit vast, wat best ongelooflijk is.
2. Zoek naar de oorzaak van de impasse
Vervolgens, voeg snel windbg toe aan het proces om het uit te zoeken.
1) Kijk naar de hoofddraad De interface reageert niet, dus natuurlijk zit de hoofddraad vast, dus je moet kijken wat de hoofddraad op dit moment doet. Gebruik het commando ~0s + !clrstack.
0:000> !clrstack OS Thread ID: 0x5a10 (0) Child SP IP-oproeplocatie 0000004d10dfde00 00007ffb889a10e4 [GCFrame: 0000004d10dfde00] 00000004d10dfdf28 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(Booleaans) 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.Bericht doorReferentie) 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] Uit de stack-uitvoer blijkt dat de hoofdthread uiteindelijk vastzit op Monitor.ObjWait onder Task.Result, wat betekent dat de laatste jsonString nog niet is opgehaald, wat erg vreemd is, het is al enkele minuten geleden, is er een probleem met het netwerk? Mijn net is 100 meter vol vuurkracht...
2) Waar is jsonString gebleven? Als jsonString op de beheerde heap wordt gevonden, betekent dit dat er iets in het programma is dat het resultaat vertraagt; gebruik het commando !dumpheap -type String -min 8500 + !do 000001f19002fcf0 om te bekijken, zoals weergegeven in de onderstaande figuur:
Uit de figuur blijkt duidelijk dat html terug is, en nu alles terug is, waarom is Task.Result dan nog niet afgelopen? De volgende stap is om te zien wie deze html heeft en !gcroot te gebruiken.
0:000> !gcroot 000001f19002fcf0 Thread 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 -> 0000001f1800716d8 System.EventHandler -> 000001f1800716b0 System.Windows.Forms.ApplicationContext -> 000001f180071780 System.EventHandler -> 000001f18006ab38 System.Windows.Forms.Application+ThreadContext -> 000001f18006b140 System.Windows.Forms.Application+MarshalingControl -> 000001f18016c9c8 System.Collecties.Wachtrij -> 000001f18016ca00 Systeem.Object[] -> 000001f18016c948 System.Windows.Forms.Control+ThreadMethodEntry -> 0000001f18016c8b8 System.Object[] -> 000001f1800e6f80 Systeem. Actie -> 000001f1800e6f60 System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner -> 000001f1800a77d0 WindowsFormsApp4.Form1+<GetJsonAsync>d__2 -> 000001f1800b4e50 System.Threading.Tasks.Task'1[[System.String, mscorlib]] -> 000001f19002fcf0 System.String
Ik heb 1 unieke wortel gevonden (run '! GCRoot - alles om alle wortels te zien). Uit de outputresultaten blijkt dat deze System.String uiteindelijk wordt vastgehouden door WindowsFormsApp4.Form1 van de 5a10-thread, en je kunt !t gebruiken om te controleren wat thread 5a10 is.
0:000> !t Slot ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 0 1 5a10 000001f1f1b01200 2026020 Preventief 000001F1800E70E8:000001F1800E7FD0 000001f1f1ad5b90 0 STA 2 2 712c 000001f1f1b2a270 2b220 Preëmptief 0000000000000000:00000000000000000 0000001f1f1ad5b90 0 MTA (Finalizer) Ik zeg, 5a10 bleek de hoofddraad te zijn, het was echt een beetje verwarrend, de hoofddraad zat vast en de draad werd vastgehouden door de hoofddraad, volledig onverklaarbaar.
3) Zoek naar breakout-punten Nog steeds rustig teruggaan en deze referentieketen bekijken, ontdekte ik dat er hier een Queue is: -> 000001f18016c9c8 System.Collections.Queue, ik heb een idee, ik kan de broncode debuggen door een breakpoint in de Queue te plaatsen, en de tool gebruikt DnSpy, doe het gewoon.
Zoals je uit de figuur kunt zien, wordt bij het betreden van de wachtrij thread 10 gebruikt, wat betekent dat de string op dit moment nog niet door de hoofdthread wordt vastgehouden.
Uit het diagram blijkt dat de continue taak uiteindelijk wordt ingepland door WindowsFormsSynchronizationContext.Post in de Wachtrij onder Controle, en de gegevens in deze wachtrij moeten worden uitgevoerd door de UI-thread, dus er is de volgende dialoog:
Hoofddraad: Taakbroer, wanneer ben je klaar met het uitvoeren? Ik wacht tot jij het signaal voltooit?
taak:Broer, als je me niet executeert, hoe kan ik het dan afmaken?
Hoofddraad:Oh...
Samengevat: de continuation-taak heeft de wachtrij bereikt die wacht tot de hoofdthread wordt uitgevoerd, en op dat moment is de hoofdthread verlamd en wacht tot de continuation-taak voltooid is=true, het probleem is hier: hoe kan de continuation-taak niet worden uitgevoerd Complete=true? Dus ze zijn zo.
Drie: Hoe je het oplost
Met kennis van oorzaak en gevolg is deze methode van scheuren eenvoudig, grofweg onderverdeeld in twee typen.
1. Het is verboden om een voortzettingstaak in een wachtrij te plaatsen
Om dit pad af te snijden, impliceert het dat de threadpool de taak zelf laat afsluiten, zodat de UI-thread kan voelen dat de taak is voltooid, en uiteindelijk kan de UI-thread de uiteindelijke html ophalen, namelijk ConfigureAwait(false) toevoegen na await, als volgt:
2. Blokkeer de hoofddraad
Als de hoofdthread niet geblokkeerd is, kan de hoofdthread vrij de taken verkrijgen die in Control.Queue uitgevoerd moeten worden, en de methode is heel eenvoudig: voeg gewoon await toe vóór GetJsonAsync.
Drie: Samenvatting
De conclusie is dat je meer van je eigen praktische praktijk moet doen, theoretische kennis wordt je met geweld bijgebracht door anderen, of het nu goed of fout is, je hebt in feite geen dieptepunt in je hart, praktische verificatie is wat echt van jou is, en het is moeilijk te vergeten dat je tenslotte echt hebt ervaren, geoefend en geverifieerd.
Origineel:De hyperlink-login is zichtbaar.
|