Acest articol este un articol oglindă al traducerii automate, vă rugăm să faceți clic aici pentru a sări la articolul original.

Vedere: 6277|Răspunde: 2

[Comunicare] [Întoarce] O propoziție Task. Rezultatul va fi blocat, altfel cum să scriu acest cod?

[Copiază linkul]
Postat la 2022-8-7 21:11:58 | | | |
1: Context

1. Să spună povești

Acum câteva zile, când am indexat articolul despre .NET Advanced Debugging pe github, am găsit un comentariu interesant, vezi articolul pentru detalii, captura de ecran este următoarea:



Probabil înseamnă că executarea Task.Result sub firul principal al Winform va duce la un blocaj; de asemenea, m-am uitat la linkul de referință din poză, Stephen este șeful absolut, dar acest articol este în principal despre îndoctrinarea unui paragraf lung de text și nu îți permite cu adevărat să vezi ceea ce vezi, așa că îl voi analiza din perspectiva windbg.

2: Analiza Windbg

1. Va fi cu adevărat blocat?

Desigur, nu am mai jucat Winform de mulți ani și nu pot să-mi dau seama dacă va juca, cel puțin nu pe consolă.

Codul este foarte simplu, rulează programul, apasă click și, într-adevăr, interfața este blocată, ceea ce este puțin incredibil.

2. Căutarea cauzei blocajului

Apoi, grăbește-te și adaugă windbg procesului pentru a afla.

1) Uită-te la firul principal
Interfața nu răspunde, așa că, în mod natural, firul principal este blocat, așa că trebuie să te uiți la ce face firul principal în acest moment. Folosește comanda ~0s + !clrstack.

0:000> !clrstack
ID thread OS: 0x5a10 (0)
        Site de apel IP SP copil
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]
Din ieșirea stack-ului, firul principal este în sfârșit blocat pe Monitor.ObjWait sub Task.Result, ceea ce înseamnă că nu a preluat încă ultimul jsonString, ceea ce este foarte ciudat, au trecut câteva minute, există vreo problemă cu rețeaua? Plasa mea are 100M plină de putere de foc...

2) Unde a dispărut jsonString?
Dacă jsonString se găsește pe heap-ul gestionat, înseamnă că există ceva în program care face ca rezultatul să întârzie, folosește comanda !dumpheap -type String -min 8500 + !do 000001f19002fcf0 pentru a vizualiza, așa cum se arată în figura de mai jos:



Din figură, se vede clar că html a revenit, iar dacă totul a revenit, de ce nu s-a încheiat încă Task.Result? Următorul pas este să vezi cine deține acest html și să folosești !gcroot.

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+ListăListă
            -> 000001f1800716d8 Sistem.EventHandler
            -> 000001f1800716b0 System.Windows.Forms.ApplicationContext
            -> 000001f180071780 Sistem.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 Sistem. Acțiune
            -> 000001f1800e6f60 System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner
            -> 000001f1800a77d0 WindowsFormsApp4.Form1+<GetJsonAsync>d__2
            -> 000001f1800b4e50 System.Threading.Tasks.Task'1[[System.String, mscorlib]]
            -> 000001f19002fcf0 System.String

Am găsit 1 rădăcină unică (fugi '! GCRoot -toate' pentru a vedea toate rădăcinile).
Din rezultatele de ieșire, acest System.String este în cele din urmă deținut de WindowsFormsApp4.Form1 din firul 5a10 și poți folosi !t pentru a verifica ce fir este 5a10.

0:000> !t Încuietoare  
       ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
   0 1 5a10 000001f1f1b01200 2026020 Preemptiv 000001F1800E70E8:000001F1800E7FD0 000001f1f1ad5b90 0 STA
   2 2 712c 000001f1f1b2a270 2b220 Preemptiv 0000000000000000:0000000000000000000 0000001f1f1ad5b90 0 MTA (Finalizer)
Am zis, 5a10 s-a dovedit a fi firul principal, a fost puțin confuz, firul principal era blocat, iar firul era ținut de firul principal, complet inexplicabil.

3) Caută puncte de ieșire
Încă mă gândesc calm la acest lanț de referință, am descoperit că există o coadă aici: -> 000001f18016c9c8 System.Collections.Queue, am o idee, pot depana codul sursă plasând un punct de întrerupere în coadă, iar instrumentul folosește DnSpy, pur și simplu fă-o.



După cum se vede din figură, când intri în Coadă, se folosește firul 10, ceea ce înseamnă că șirul nu este încă deținut de firul principal în acest moment.



Din diagramă, se poate observa că sarcina continuă este în cele din urmă programată de WindowsFormsSynchronizationContext.Post în Coada sub Control, iar datele din această coadă trebuie executate de firul UI, deci există următorul dialog:

Firul principal: Sarcină, frate, când termini de executat? Aștept să termini semnalul?

sarcină:Frate, dacă nu mă execuți, cum să termin?

Firul principal:Oh...

În rezumat: sarcina de continuare a ajuns la coadă, așteptând execuția firului principal, iar în acest moment, firul principal este paralizat și a așteptat ca sarcina de continuare să se finalizeze=adevărat, problema este aici: cum poate sarcina de continuare să nu fie executată Complet=adevărat? Deci sunt așa.

Trei: Cum să o spargi

Cunoscând cauza și efectul, această metodă de crăpare este simplă, împărțită aproximativ în două tipuri.

1. Este interzis să introduci o sarcină de continuare într-o coadă

Pentru a tăia această cale, implicația este să lăsăm pool-ul de fire să încheie sarcina de unul singur, astfel încât thread-ul UI să poată simți că sarcina a fost finalizată, iar în final thread-ul UI să poată primi fișierul html final, adică să adauge ConfigureAwait(false) după await, după cum urmează:



2. Blochează firul principal

Dacă firul principal nu este blocat, atunci firul principal poate obține liber sarcinile care trebuie executate în Control.Queue, iar metoda este foarte simplă, doar adaugă await înainte de GetJsonAsync.



Trei: Rezumat

Concluzia este să faci mai multă practică proprie, cunoașterea teoretică îți este insuflată cu forța de către ceilalți, fie că este corectă sau greșită, de fapt, nu ai fund în inimă, verificarea practică este ceea ce îți aparține cu adevărat și este greu să uiți, până la urmă ai experimentat, ai practicat și verificat cu adevărat.

Original:Autentificarea cu hyperlink este vizibilă.





Precedent:Docker modifică și restricționează CPU-ul containerului, memoria și alte resurse
Următor:Array-urile JS sunt diferența și utilizarea fiecăruia și câte
Postat la 2022-8-8 17:01:32 |
Învață să înveți...
Postat la 2022-9-2 11:39:19 |
Învață puțin
Disclaimer:
Tot software-ul, materialele de programare sau articolele publicate de Code Farmer Network sunt destinate exclusiv scopurilor de învățare și cercetare; Conținutul de mai sus nu va fi folosit în scopuri comerciale sau ilegale, altfel utilizatorii vor suporta toate consecințele. Informațiile de pe acest site provin de pe Internet, iar disputele privind drepturile de autor nu au legătură cu acest site. Trebuie să ștergi complet conținutul de mai sus de pe calculatorul tău în termen de 24 de ore de la descărcare. Dacă îți place programul, te rugăm să susții software-ul autentic, să cumperi înregistrarea și să primești servicii autentice mai bune. Dacă există vreo încălcare, vă rugăm să ne contactați prin e-mail.

Mail To:help@itsvse.com