1. Pagrindiniai faktai
1. Pasakokite istorijas
Prieš kelias dienas, kai indeksavau straipsnį apie .NET išplėstinį derinimą į github, radau įdomų komentarą, daugiau informacijos rasite straipsnyje, ekrano kopija yra tokia:
Tikriausiai tai reiškia, kad Task.Result vykdymas pagal pagrindinę Winform temą sukels aklavietę, aš taip pat pažiūrėjau į nuorodą paveikslėlyje, Stephenas yra absoliutus bosas, tačiau šis straipsnis daugiausia yra apie didelės teksto pastraipos indoktrinaciją, ir jis tikrai neleidžia pamatyti to, ką matote, todėl analizuosiu jį iš windbg perspektyvos.
2: Windbg analizė
1. Ar jis tikrai bus aklavietėje?
Žinoma, aš nežaidžiau winform daugelį metų, ir aš negaliu suprasti, ar tai bus, bent jau ne konsolėje.
Kodas yra labai paprastas, paleiskite programą, spustelėkite spustelėkite, ir tikrai pakankamai, sąsaja yra įstrigęs, o tai yra šiek tiek neįtikėtina.
2. Ieškokite aklavietės priežasties
Tada paskubėkite ir pritvirtinkite windbg prie proceso, kad sužinotumėte.
1) Pažvelkite į pagrindinę temą Sąsaja nereaguoja, todėl natūralu, kad pagrindinė gija įstrigo, todėl reikia pasidomėti, ką šiuo metu veikia pagrindinė gija. Naudokite komandą ~0s + !clrstack.
0:000> !clrstack OS gijos ID: 0x5a10 (0) Vaiko SP IP skambučių svetainė 0000004d10dfde00 00007ffb889a10e4 [GCFrame: 0000004d10dfde00] 0000004d10dfdf28 00007ffb889a10e4 [HelperMethodFrame_1OBJ: 0000004d10dfdf28] System.Threading.Monitor.ObjWait(Bulio, 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(Bulio logika) 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] Iš rietuvės išvesties pagrindinė gija pagaliau įstrigo Monitor.ObjWait skiltyje Task.Result, o tai reiškia, kad ji dar negavo paskutinio jsonString, o tai labai keista, praėjo kelios minutės, ar yra problemų su tinklu? Mano tinklas yra 100M pilnas ugnies galios...
2) Kur dingo jsonString? Jei valdomoje krūvoje randama jsonString, tai reiškia, kad programoje yra kažkas, dėl ko rezultatas vėluoja, naudokite komandą !dumpheap -type String -min 8500 + !do 000001f19002fcf0, kad peržiūrėtumėte, kaip parodyta paveikslėlyje žemiau:
Iš paveikslėlio aiškiai matyti, kad html grįžo, nes viskas grįžo, kodėl dar nesibaigė Task.Result? Kitas žingsnis yra pamatyti, kas turi šį html ir naudoti !gcroot.
0:000> !gcroot 000001f19002fcf0 5a10 sriegis: 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
Rasta 1 unikalių šaknų (paleisti '! GCRoot -all' pamatyti visas šaknis). Iš išvesties rezultatų šią System.String galiausiai laiko WindowsFormsApp4.Form1 iš 5a10 gijos, ir galite naudoti !t, kad patikrintumėte, kas yra 5a10 gija.
0:000> !t Užraktas ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 0 1 5a10 000001f1f1b01200 2026020 Prevencinis 000001F1800E70E8:000001F1800E7FD0 000001f1f1ad5b90 0 STA 2 2 712c 000001f1f1b2a270 2b220 Prevencinis 00000000000000000:000000000000000000 0000001f1f1ad5b90 0 MTA (Finalizatorius) Aš einu, 5a10 pasirodė esąs pagrindinis siūlas, tai buvo tikrai šiek tiek painu, pagrindinis siūlas buvo įstrigęs, o styga buvo laikoma pagrindine gija, visiškai nepaaiškinama.
3) Ieškokite lūžio taškų Vis dar grįždamas atgal ir ramiai galvodamas apie šią nuorodų grandinę, radau, kad čia yra eilė: -> 000001f18016c9c8 System.Collections.Queue, turiu idėją, galiu derinti šaltinio kodą įdėdamas lūžio tašką į eilę, o įrankis naudoja DnSpy, tiesiog daryk tai.
Kaip matote iš paveikslėlio, įeinant į eilę, naudojama 10 gija, o tai reiškia, kad šiuo metu eilutės dar nelaiko pagrindinė gija.
Iš diagramos matyti, kad nepertraukiamą užduotį galiausiai suplanuoja WindowsFormsSynchronizationContext.Post į valdomą eilę, o šios eilės duomenis turi vykdyti vartotojo sąsajos gija, todėl yra šis dialogo langas:
Pagrindinė gija: Užduotis broli, kada baigsite ją vykdyti? Laukiu, kol užbaigsi signalą?
užduotis:Broli, jei tu manęs neįvykdysi, kaip aš galėsiu ją užbaigti?
Pagrindinė gija:Oh...
Apibendrinant: tęsimo užduotis pasiekė eilę, laukiančią, kol bus įvykdyta pagrindinė gija, o šiuo metu pagrindinė gija yra apstulbusi ir laukė, kol tęsinio užduotis bus baigta=teisinga, problema yra čia: kaip negalima vykdyti tęsinio užduoties Complete=true? Taigi jie yra tokie.
Trys: Kaip jį nulaužti
Žinant priežastį ir pasekmę, šis įtrūkimo būdas yra paprastas, apytiksliai suskirstytas į du tipus.
1. Draudžiama įmesti tęsinio užduotį į eilę
Norint nutraukti šį kelią, reiškia, kad gijų telkinys pats užbaigia užduotį, kad vartotojo sąsajos gija galėtų pajusti, kad užduotis buvo atlikta, ir galiausiai vartotojo sąsajos gija gali gauti galutinį html, t. y. pridėti ConfigureAwait(false) po laukimo, taip:
2. Užblokuokite pagrindinį siūlą
Jei pagrindinė gija nėra užblokuota, tada pagrindinė gija gali laisvai gauti užduotis, kurias reikia atlikti "Control.Queue", o metodas yra labai paprastas, tiesiog pridėkite laukti prieš "GetJsonAsync".
Trys: santrauka
Išvada yra ta, kad atlikite daugiau savo praktinės praktikos, teorines žinias jums prievarta skiepija kiti, nesvarbu, ar tai teisinga, ar neteisinga, iš tikrųjų jūs neturite dugno širdyje, praktinis patikrinimas yra tai, kas jums iš tikrųjų priklauso, ir sunku pamiršti, juk jūs tikrai patyrėte, praktikavote ir patikrinote.
Originalus:Hipersaito prisijungimas matomas.
|