1: Bakgrund
1. Berätta historier
För några dagar sedan, när jag indexerade artikeln om .NET Advanced Debugging till github, hittade jag en intressant kommentar, se artikeln för detaljer, skärmdumpen är följande:
Det betyder troligen att om man kör Task.Result under huvudtråden i Winform kommer det att orsaka ett deadlock, jag tittade också på referenslänken på bilden, Stephen är den absoluta chefen, men den här artikeln handlar främst om indoktrinering av ett stort stycke text, och den låter dig egentligen inte se det du ser, så jag kommer att analysera den ur windbg:s perspektiv.
2: Windbg-analys
1. Kommer det verkligen att vara låst?
Självklart har jag inte spelat Winform på många år, och jag kan inte avgöra om det kommer att fungera, åtminstone inte på konsolen.
Koden är väldigt enkel, kör programmet, klicka klick, och mycket riktigt fastnar gränssnittet, vilket är ganska otroligt.
2. Sök orsaken till dödläget
Nästa steg, skynda dig och koppla windbg till processen för att ta reda på det.
1) Titta på huvudtråden Gränssnittet svarar inte, så naturligtvis fastnar huvudtråden, så du behöver titta på vad huvudtråden gör just nu. Använd kommandot ~0s + !clrstack.
0:000> !clrstack OS-tråd-ID: 0x5a10 (0) Child SP IP-anropsplats 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.InternalWait(Int32, System.Threading.CancellationToken) 0000004d10dfe210 00007ffb6725cfa7 System.Threading.Tasks.Task'1[[System.__Canon, mscorlib]]. GetResultCore (boolesk) 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.Meddelande 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] Från stackens utgång fastnar huvudtråden slutligen på Monitor.ObjWait under Task.Result, vilket betyder att den ännu inte har hämtat den senaste jsonString, vilket är väldigt märkligt, det har gått flera minuter, är det något problem med nätverket? Mitt nät är 100 meter fullt av eldkraft...
2) Vart tog jsonString vägen? Om jsonString hittas på den hanterade heapen betyder det att det finns något i programmet som gör att resultatet fördröjs, använd kommandot !dumpheap -type String -min 8500 + !do 000001f19002fcf0 för att se, som visas i figuren nedan:
Från figuren kan man tydligt se att html är tillbaka, och eftersom allt är tillbaka, varför har inte Task.Result avslutats än? Nästa steg är att se vem som har denna html och använda !gcroot.
0:000> !gcroot 0000001f19002fcf0 Tråd 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.KomponentModell.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 -> 0000001f18016c9c8 System.Collections.Queue -> 000001f18016ca00 System.Syfte[] -> 000001f18016c948 System.Windows.Forms.Control+ThreadMethodEntry -> 000001f18016c8b8 System.Object[] -> 000001f1800e6f80 System. Åtgärd -> 000001f1800e6f60 System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner -> 000001f1800a77d0 WindowsFormsApp4.Form1+<GetJsonAsync>d__2 -> 000001f1800b4e50 System.Threading.Tasks.Task'1[[System.String, mscorlib]] -> 000001f19002fcf0 System.Sträng
Hittade 1 unik rött (spring! GCRoot – alla för att se alla rötter). Utifrån utdata hålls denna System.String slutligen av WindowsFormsApp4.Form1 i 5a10-tråden, och du kan använda !t för att verifiera vad tråd 5a10 är.
0:000> !t Lås ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 0 1 5a10 000001f1f1b01200 2026020 Förebyggande 000001F1800E70E8:000001F1800E7FD0 000001f1f1ad5b90 0 STA 2 2 712c 000001f1f1b2a270 2b220 Preemptive 00000000000000000:0000000000000000 0000001f1f1ad5b90 0 MTA (Finalizer) Jag säger, 5a10 visade sig vara huvudtråden, det var verkligen lite förvirrande, huvudtråden fastnade och tråden hölls fast av huvudtråden, helt oförklarligt.
3) Leta efter utbrottspunkter När jag fortfarande går tillbaka och lugnt tänker på denna referenskedja, upptäckte jag att det finns en kö här: -> 000001f18016c9c8 System.Collections.Queue, jag har en idé, jag kan felsöka källkoden genom att placera en brytpunkt i kön, och verktyget använder DnSpy, bara gör det.
Som du kan se i figuren används tråd 10 när man går in i kön, vilket betyder att strängen ännu inte hålls av huvudtråden just nu.
Från diagrammet kan man se att den kontinuerliga uppgiften slutligen schemaläggs av WindowsFormsSynchronizationContext.Post in i kön under kontroll, och datan i denna kö måste köras av UI-tråden, så det finns följande dialog:
Huvudtråd: Uppgift bror, när ska du bli klar med att utföra den? Jag väntar på att du ska slutföra signalen?
uppgift:Bror, om du inte avrättar mig, hur ska jag då kunna avsluta det?
Huvudtråd:Åh...
Sammanfattningsvis: fortsättningsuppgiften har nått kön som väntar på att huvudtråden ska köras, och vid denna tidpunkt är huvudtråden bedövad och har väntat på att fortsättningsuppgiften ska slutföras=sant, problemet är här: hur kan fortsättningsuppgiften inte köras Komplett=sann? Så de är så här.
Tre: Hur man knäcker den
Med tanke på orsak och verkan är denna sprickmetod enkel och ungefär uppdelad i två typer.
1. Det är förbjudet att lägga en fortsättningsuppgift i en kö
För att stänga av denna väg innebär det att trådpoolen själv avslutar uppgiften, så att UI-tråden kan känna av att uppgiften är slutförd, och slutligen kan UI-tråden få den slutliga html:en, vilket är att lägga till ConfigureAwait(false) efter await, enligt följande:
2. Blockera huvudtråden
Om huvudtråden inte blockeras kan huvudtråden fritt hämta de uppgifter som behöver köras i Control.Queue, och metoden är mycket enkel, lägg bara till await innan GetJsonAsync.
Tre: Sammanfattning
Slutsatsen är att göra mer av din egen praktiska praktik, teoretisk kunskap tvingas in i dig av andra, oavsett om den är rätt eller fel, faktiskt har du ingen botten i ditt hjärta, praktisk verifiering är det som verkligen tillhör dig, och det är svårt att glömma, trots allt har du verkligen upplevt, övat och verifierat.
Original:Inloggningen med hyperlänken är synlig.
|