Tento článok je zrkadlovým článkom o strojovom preklade, kliknite sem pre prechod na pôvodný článok.

Pohľad: 6277|Odpoveď: 2

[Komunikácia] [Otoč] Veta Úloha. Výsledok bude zamknutý, ako inak napísať tento kód?

[Kopírovať odkaz]
Zverejnené 7. 8. 2022 o 21:11:58 | | | |
1: Pozadie

1. Rozprávaj príbehy

Pred pár dňami, keď som indexoval článok o .NET Advanced Debugging na github, našiel som zaujímavý komentár, podrobnosti nájdete v článku, screenshot je nasledovný:



Pravdepodobne to znamená, že spustenie Task.Result v hlavnom vlákne Winformu spôsobí patovú situáciu, pozrel som sa aj na referenčný odkaz na obrázku, Stephen je absolútny šéf, ale tento článok je hlavne o indoktrinácii veľkého odseku textu a naozaj vám neumožňuje vidieť to, čo vidíte, takže to analyzujem z pohľadu windbg.

2: Analýza Windbg

1. Bude to naozaj zablokované?

Samozrejme, Winform som nehral už mnoho rokov a neviem prísť na to, či to tak bude, aspoň nie na konzole.

Kód je veľmi jednoduchý, spustíš program, klikneš klik a naozaj, rozhranie je zaseknuté, čo je naozaj neuveriteľné.

2. Hľadajte príčinu patovej situácie

Potom sa ponáhľajte a pripojte windbg k procesu, aby ste to zistili.

1) Pozri sa na hlavnú tému
Rozhranie je nereagujúce, takže hlavné vlákno je prirodzene zaseknuté, takže sa musíte pozrieť, čo hlavné vlákno práve robí. Použite príkaz ~0s + !clrstack.

0:000> !clrstack
ID OS vlákna: 0x5a10 (0)
        Child SP IP volacie miesto
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]
Z výstupu zásobníka je hlavné vlákno konečne zaseknuté na Monitor.ObjWait pod Task.Result, čo znamená, že ešte nenačítalo posledný jsonString, čo je veľmi zvláštne, už je to niekoľko minút, je problém so sieťou? Moja sieť je 100M plná palebnej sily...

2) Kam zmizol jsonString?
Ak sa jsonString nájde na spravovanej halde, znamená to, že v programe je niečo, čo spôsobuje oneskorenie výsledku, použite príkaz !dumpheap -type String -min 8500 + !do 000001f19002fcf0 na zobrazenie, ako je znázornené na obrázku nižšie:



Z obrázka je jasne vidieť, že html je späť, keďže je všetko späť, prečo Task.Result ešte neskončil? Ďalším krokom je zistiť, kto drží tento html a použiť !gcroot.

0:000> !gcroot 000001f19002fcf0
Vlákno 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 Systém. Objekt[]
            -> 000001f18016c948 System.Windows.Forms.Control+ThreadMethodEntry
            -> 000001f18016c8b8 System.Object[]
            -> 000001f1800e6f80 Systém. Akcia
            -> 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šiel som 1 unikátny koreň (run '! GCRoot -all' na zobrazenie všetkých koreňov).
Z výstupných výsledkov je tento System.String nakoniec uložený vo WindowsFormsApp4.Form1 vlákna 5a10 a môžete použiť !t na overenie, čo je vlákno 5a10.

0:000> !t Zámok  
       ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
   0 1 5a10 000001f1f1b01200 2026020 Preemptívne 000001F1800E70E8:000001F1800E7FD0 000001f1f1ad5b90 0 STA
   2 2 712c 0000001f1f1f1b2a270 2b220 Preemptívne 0000000000000000000:000000000000000000000 0000001f1f1ad5b90 0 MTA (Finalizer)
Povedal som, 5a10 sa ukázalo byť hlavnou niťou, bolo to naozaj trochu mätúce, hlavná niť bola zaseknutá a niť držala hlavná niť, úplne nevysvetliteľné.

3) Hľadajte body prerazenia
Stále sa vraciam a pokojne premýšľam o tomto referenčnom reťazci, ale zistil som, že tu je Queue: -> 000001f18016c9c8 System.Collections.Queue, mám nápad, môžem ladiť zdrojový kód tak, že do Queue umiestnim breakpoint, a nástroj používa DnSpy, jednoducho to urob.



Ako vidíte na obrázku, pri vstupe do Queue sa používa vlákno 10, čo znamená, že reťazec ešte nie je v danom čase držaný hlavným vláknom.



Z diagramu je vidieť, že kontinuálna úloha je nakoniec naplánovaná WindowsFormsSynchronizationContext.Post do Fronty pod Kontrolou a dáta v tejto Fronte musia byť vykonané vláknom UI, takže je tu nasledujúci dialóg:

Hlavná téma: Úloha, brat, kedy ju dokončíš? Čakám, kým dokončíš signál?

úloha:Brat, ak ma nepopravíš, ako to mám dokončiť?

Hlavná diskusia:Oh...

Zhrnutie: úloha pokračovania dosiahla frontu čakajúcu na vykonanie hlavného vlákna a v tomto momente je hlavné vlákno omráčené a čaká na dokončenie úlohy pokračovania =true, problém je tu: ako je možné, že úloha pokračovania nemôže byť vykonaná Complete=true? Takže sú takíto.

Tretie: Ako to rozlúštiť

Poznajúc príčinu a následok, táto metóda praskania je jednoduchá, približne rozdelená na dva typy.

1. Je zakázané vkladať pokračovateľskú úlohu do fronty

Na ukončenie tejto cesty znamená nechať thread pool ukončiť úlohu sám, aby vlákno UI mohlo vnímať, že úloha bola dokončená, a nakoniec UI vlákno získa finálny html, čo znamená pridať ConfigureAwait(false) po await, nasledovne:



2. Zablokujte hlavné vlákno

Ak hlavné vlákno nie je zablokované, hlavné vlákno môže voľne získať úlohy, ktoré je potrebné vykonať v Control.Queue, a metóda je veľmi jednoduchá, stačí pridať await pred GetJsonAsync.



Tretie: Zhrnutie

Záver je, že ak budete viac praktizovať sami, teoretické vedomosti vám násilne vštepujú iní, či už sú správne alebo nesprávne, v skutočnosti nemáte v srdci dno, praktické overovanie je to, čo vám naozaj patrí, a je ťažké zabudnúť, veď ste naozaj zažili, precvičili a overili.

Originál:Prihlásenie na hypertextový odkaz je viditeľné.





Predchádzajúci:Docker upravuje a obmedzuje CPU, pamäť a ďalšie zdroje kontajnera
Budúci:JS polia sú rozdielom a využitím každého a niektorých
Zverejnené 8.8.2022 o 17:01:32 |
Nauč sa učiť...
Zverejnené 2022-9-2 11:39:19 |
Nauč sa trochu
Vyhlásenie:
Všetok softvér, programovacie materiály alebo články publikované spoločnosťou Code Farmer Network slúžia len na vzdelávacie a výskumné účely; Vyššie uvedený obsah nesmie byť použitý na komerčné alebo nezákonné účely, inak nesú všetky následky používateľmi. Informácie na tejto stránke pochádzajú z internetu a spory o autorské práva s touto stránkou nesúvisia. Musíte úplne vymazať vyššie uvedený obsah zo svojho počítača do 24 hodín od stiahnutia. Ak sa vám program páči, podporte originálny softvér, zakúpte si registráciu a získajte lepšie originálne služby. Ak dôjde k akémukoľvek porušeniu, kontaktujte nás prosím e-mailom.

Mail To:help@itsvse.com