Questo articolo è un articolo speculare di traduzione automatica, clicca qui per saltare all'articolo originale.

Vista: 6277|Risposta: 2

[Comunicazione] [Gira] Una frase Compito. Il risultato sarà bloccato, come altro scrivere questo codice?

[Copiato link]
Pubblicato il 7-8-2022 21:11:58 | | | |
1: Contesto

1. Raccontare storie

Qualche giorno fa, quando ho indicizzato l'articolo su .NET Advanced Debugging su github, ho trovato un commento interessante, vedi l'articolo per i dettagli, lo screenshot è il seguente:



Probabilmente significa che eseguire Task.Result sotto il thread principale di Winform causerà uno stallo; ho anche guardato il link di riferimento nella foto, Stephen è il boss assoluto, ma questo articolo riguarda principalmente l'indottrinamento di un lungo paragrafo di testo, e non ti permette davvero di vedere ciò che vedi, quindi lo analizzerò dal punto di vista di windbg.

2: Analisi Windbg

1. Sarà davvero bloccato?

Ovviamente, non gioco a Winform da molti anni e non riesco a capire se lo farà, almeno non sulla console.

Il codice è molto semplice, esegui il programma, clicca clicca, e infatti l'interfaccia è bloccata, il che è un po' incredibile.

2. Cercare la causa dello stallo

Poi, sbrigati ad aggiungere windbg al processo per scoprirlo.

1) Guarda il thread principale
L'interfaccia non risponde, quindi naturalmente il thread principale è bloccato, quindi devi guardare cosa sta facendo il thread principale in questo momento. Usa il comando ~0s + !clrstack.

0:000> !clrstack
ID thread OS: 0x5a10 (0)
        Sito di chiamata IP SP figlio
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]
Dall'output dello stack, il thread principale è finalmente bloccato su Monitor.ObjWait sotto Task.Result, il che significa che non ha ancora recuperato l'ultima jsonString, il che è molto strano, sono passati diversi minuti, c'è un problema con la rete? La mia rete è piena di 100 metri di potenza di fuoco...

2) Dov'è andato jsonString?
Se jsonString si trova sull'heap gestito, significa che c'è qualcosa nel programma che fa ritardare il risultato, usa il comando !dumpheap -type String -min 8500 + !do 000001f19002fcf0 per visualizzare, come mostrato nella figura sotto:



Dalla figura si vede chiaramente che html è tornato, visto che è tutto tornato, perché Task.Result non è ancora terminato? Il passo successivo è vedere chi detiene questo html e usare !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+ListEntry
            -> 000001f1800716d8 Sistema.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 Sistema.Oggetto[]
            -> 000001f1800e6f80 Sistema. Azione
            -> 000001f1800e6f60 System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner
            -> 000001f1800a77d0 WindowsFormsApp4.Form1+<GetJsonAsync>d__2
            -> 000001f1800b4e50 System.Threading.Tasks.Task'1[[System.String, mscorlib]]
            -> 000001f19002fcf0 System.String

Ho trovato 1 radice uniche (corri '! GCRoot -all' per vedere tutte le radici).
Dai risultati di output, questa System.String è infine detenuta da WindowsFormsApp4.Form1 del thread 5a10, e puoi usare !t per verificare quale thread sia 5a10.

0:000> 000 Blocco  
       ID OSID ThreadOBJ State GC Mode GC Alloc Contesto Dominio Eccezione Apt
   0 1 5a10 000001f1f1b01200 2026020 Preemptivo 000001F1800E70E8:000001F1800E7FD0 000001f1f1ad5b90 0 STA
   2 2 712c 000001f1f1b2a270 2b220 Preemptivo 000000000000000:0000000000000000000000 0000001f1f1ad5b90 0 MTA (Finalizer)
Ho detto, il filo 5a10 si è rivelato essere il filo principale, è stato davvero un po' confuso, il filo principale era bloccato e il filo era tenuto dal filo principale, completamente inspiegabile.

3) Cerca punti di rottura
Ripensando ancora con calma a questa catena di riferimento, ho scoperto che qui c'è una Queue: -> 000001f18016c9c8 System.Collections.Queue, ho un'idea, posso fare il debug del codice sorgente posizionando un punto di interruzione nella Queue, e lo strumento usa DnSpy, basta farlo.



Come si può vedere dalla figura, entrando in Coda, si usa il thread 10, il che significa che la stringa non è ancora tenuta dal thread principale in questo momento.



Dal diagramma si può vedere che il compito continuo viene finalmente programmato da WindowsFormsSynchronizationContext.Post nella Coda sotto Control, e i dati in questa Coda devono essere eseguiti dal thread UI, quindi c'è il seguente dialogo:

Thread principale: compito fratello, quando finirai di eseguirlo? Sto aspettando che tu completi il segnale?

compito:Fratello, se non mi uccidi, come posso finirla?

Thread principale:Oh...

In sintesi: il compito di continuazione è arrivato in coda in attesa che il thread principale venga eseguito, e in questo momento il thread principale è stordito e sta aspettando che il compito di continuazione si completi=vero, il problema è qui: come può il compito di continuazione non essere eseguito Completato=vero? Quindi sono così.

Tre: Come sbloccarla

Conoscendo causa ed effetto, questo metodo di crepa è semplice, suddiviso grosso modo in due tipi.

1. È vietato inserire un compito di continuazione in una coda

Per interrompere questo percorso, l'implicazione è lasciare che il pool di thread concluda il compito da solo, così che il thread UI possa percepire che il compito è stato completato, e infine il thread UI possa ottenere l'html finale, che consiste nell'aggiungere ConfigureAwait(false) dopo await, come segue:



2. Blocca il filo principale

Se il thread principale non è bloccato, allora il thread principale può liberamente ottenere i compiti che devono essere eseguiti in Control.Queue, e il metodo è molto semplice: basta aggiungere await prima di GetJsonAsync.



Tre: Sommario

La conclusione è che fai più pratica pratica tua, la conoscenza teorica ti viene imposta con forza dagli altri, che sia giusta o sbagliata, in realtà non hai fondo nel cuore, la verifica pratica è ciò che ti appartiene davvero, ed è difficile dimenticare, dopotutto, che hai davvero vissuto, praticato e verificato.

Originale:Il login del link ipertestuale è visibile.





Precedente:Docker modifica e limita CPU, memoria e altre risorse del container
Prossimo:Gli array JS sono la differenza e l'uso di ogni e alcuni
Pubblicato il 8-8-2022 17:01:32 |
Impara a imparare...
Pubblicato il 2022-9-2 11:39:19 |
Impara un po'
Disconoscimento:
Tutto il software, i materiali di programmazione o gli articoli pubblicati dalla Code Farmer Network sono destinati esclusivamente all'apprendimento e alla ricerca; I contenuti sopra elencati non devono essere utilizzati per scopi commerciali o illegali, altrimenti gli utenti dovranno sostenere tutte le conseguenze. Le informazioni su questo sito provengono da Internet, e le controversie sul copyright non hanno nulla a che fare con questo sito. Devi eliminare completamente i contenuti sopra elencati dal tuo computer entro 24 ore dal download. Se ti piace il programma, ti preghiamo di supportare software autentico, acquistare la registrazione e ottenere servizi autentici migliori. In caso di violazione, vi preghiamo di contattarci via email.

Mail To:help@itsvse.com