Dieser Artikel ist ein Spiegelartikel der maschinellen Übersetzung, bitte klicken Sie hier, um zum Originalartikel zu springen.

Ansehen: 6277|Antwort: 2

[Kommunikation] [Drehen] Ein Satz Aufgabe. Das Ergebnis wird gesperrt, wie sonst schreibt man diesen Code?

[Link kopieren]
Veröffentlicht am 07.08.2022, 21:11:58 | | | |
1: Hintergrund

1. Geschichten erzählen

Vor ein paar Tagen, als ich den Artikel über .NET Advanced Debugging auf GitHub indexiert habe, fand ich einen interessanten Kommentar, siehe den Artikel für Details, der Screenshot ist wie folgt:



Das bedeutet wahrscheinlich, dass das Ausführen von Task.Result im Hauptthread von Winform zu einem Deadlock führt. Ich habe mir auch den Referenzlink im Bild angesehen, Stephen ist der absolute Boss, aber dieser Artikel handelt hauptsächlich von der Indoktrination eines großen Textabschnitts, und man kann nicht wirklich sehen, was man sieht, daher werde ich ihn aus der Perspektive von windbg analysieren.

2: Windbg-Analyse

1. Wird es wirklich feststehen?

Natürlich habe ich seit vielen Jahren kein Winform mehr gespielt und kann nicht herausfinden, ob es das tun wird, zumindest nicht auf der Konsole.

Der Code ist sehr einfach: Starte das Programm, klicke klick, und tatsächlich klemmt die Benutzeroberfläche, was ziemlich beeindruckend ist.

2. Suchen Sie nach der Ursache der Pattsituation

Beeilen Sie sich und hängen Windbg dem Prozess zu, um es herauszufinden.

1) Schau dir den Hauptthread an
Die Benutzeroberfläche reagiert nicht, daher bleibt der Hauptthread natürlich hängen, also musst du dir anschauen, was der Hauptthread gerade macht. Benutze den Befehl ~0s + !clrstack.

0:000> !clrstack
OS-Thread-ID: 0x5a10 (0)
        Child SP IP-Anrufsite
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]
Aus der Stack-Ausgabe ist der Hauptthread schließlich auf Monitor.ObjWait unter Task.Result hängen geblieben, was bedeutet, dass der letzte jsonString noch nicht abgerufen wurde, was sehr seltsam ist, es sind schon mehrere Minuten vergangen. Gibt es ein Problem mit dem Netzwerk? Mein Netz ist 100 Meter voller Feuerkraft...

2) Wo ist jsonString hin?
Wenn jsonString auf dem verwalteten Heap gefunden wird, bedeutet das, dass etwas im Programm das Ergebnis verzögert; verwenden Sie den Befehl !dumpheap -type String -min 8500 + !do 000001f19002fcf0, um anzuzeigen, wie in der untenstehenden Abbildung gezeigt:



Aus der Abbildung ist deutlich zu erkennen, dass HTML zurück ist, da alles wieder da ist, warum ist Task.Result noch nicht beendet? Der nächste Schritt ist, herauszufinden, wer dieses HTML hält, und !gcroot zu verwenden.

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+ListEintrag
            -> 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.Objekt[]
            -> 000001f18016c948 System.Windows.Forms.Control+ThreadMethodEntry
            -> 000001f18016c8b8 System.Objekt[]
            -> 000001f1800e6f80 System. Aktion
            -> 000001f1800e6f60 System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner
            -> 000001f1800a77d0 WindowsFormsApp4.Form1+<GetJsonAsync>d__2
            -> 000001f1800b4e50 System.Threading.Tasks.Task'1[[System.String, mscorlib]]
            -> 000001f19002fcf0 System.String

Ich habe 1 einzigartige Wurzel gefunden (lauf '! GCRoot – alles, um alle Wurzeln zu sehen).
Aus den Ausgabeergebnissen wird dieser System.String schließlich von WindowsFormsApp4.Form1 des 5a10-Threads gehalten, und du kannst !t verwenden, um zu überprüfen, welcher Thread 5a10 ist.

0:000> !t Sperre  
       ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
   0 1 5a10 000001f1f1b01200 2026020 Präventiv 000001F1800E70E8:000001F1800E7FD0 000001f1f1ad5b90 0 STA
   2 2 712c 000001f1f1b2a270 2b220 Präemptiv 000000000000000:00000000000000000 0000001f1f1ad5b90 0 MTA (Finalizer)
Ich gehe hin, 5a10 stellte sich als Hauptfaden heraus, es war wirklich etwas verwirrend, der Hauptthread steckte fest, und der Faden wurde vom Hauptthread gehalten, völlig unerklärlich.

3) Suche nach Ausbruchspunkten
Beim ruhigen Nachdenken über diese Referenzkette habe ich festgestellt, dass es hier eine Warteschlange gibt: -> 000001f18016c9c8 System.Collections.Queue, ich habe eine Idee, ich kann den Quellcode debuggen, indem ich einen Breakpoint in der Warteschlange platziere, und das Tool verwendet DnSpy, mach es einfach.



Wie Sie in der Abbildung sehen können, wird beim Eintritt in die Warteschlange Thread 10 verwendet, was bedeutet, dass der String zu diesem Zeitpunkt noch nicht vom Hauptthread gehalten wird.



Aus dem Diagramm ist zu erkennen, dass die kontinuierliche Aufgabe schließlich von WindowsFormsSynchronizationContext.Post in die unter Kontrolle gestellte Warteschlange geplant wird und die Daten in dieser Warteschlange vom UI-Thread ausgeführt werden müssen, sodass folgender Dialog folgt:

Hauptthread: Aufgabe, Bruder, wann wirst du sie fertig machen? Ich warte darauf, dass du das Zeichen fertigstellst?

Aufgabe:Bruder, wenn du mich nicht hinrichtest, wie soll ich es dann beenden?

Hauptthread:Oh...

Zusammengefasst: Die Fortsetzungsaufgabe hat die Warteschlange erreicht, die auf die Ausführung des Hauptthreads wartet, und zu diesem Zeitpunkt ist der Hauptthread betäubt und wartet darauf, dass die Fortsetzungsaufgabe=true=true abgeschlossen ist. Das Problem liegt hier: Wie kann die Fortsetzungsaufgabe nicht Complete=true ausgeführt werden? Also sind sie so.

Drittens: Wie man es knackt

Unter Kenntnis von Ursache und Wirkung ist diese Crackmethode einfach und grob in zwei Typen unterteilt.

1. Es ist verboten, eine Fortsetzungsaufgabe in eine Warteschlange zu legen

Um diesen Pfad abzuschneiden, bedeutet die Implikation, dass der Threadpool die Aufgabe selbst beenden kann, sodass der UI-Thread erkennt, dass die Aufgabe abgeschlossen wurde, und schließlich der UI-Thread das finale HTML erhalten kann, nämlich ConfigureAwait(false) nach await hinzuzufügen, wie folgt:



2. Blockieren Sie den Hauptfaden

Wenn der Hauptthread nicht blockiert ist, kann der Hauptthread die Aufgaben, die in Control.Queue ausgeführt werden müssen, frei abrufen, und die Methode ist sehr einfach, einfach await vor GetJsonAsync hinzufügen.



Drei: Zusammenfassung

Die Schlussfolgerung ist, dass man mehr eigene praktische Praxis betreibt, theoretisches Wissen wird einem von anderen gewaltsam vermittelt, egal ob es richtig oder falsch ist, tatsächlich hat man keinen Grund im Herzen, praktische Verifizierung gehört wirklich einem zu, und es ist schwer zu vergessen, dass man wirklich erlebt, geübt und überprüft hat.

Original:Der Hyperlink-Login ist sichtbar.





Vorhergehend:Docker modifiziert und beschränkt Container-CPU, Speicher und andere Ressourcen
Nächster:JS-Arrays sind der Unterschied und die Verwendung von jedem und einigen
Veröffentlicht am 8.8.2022, 17:01:32 |
Lerne zu lernen...
Veröffentlicht am 2.9.2022, 11:39:19 |
Lerne ein bisschen
Verzichtserklärung:
Alle von Code Farmer Network veröffentlichten Software, Programmiermaterialien oder Artikel dienen ausschließlich Lern- und Forschungszwecken; Die oben genannten Inhalte dürfen nicht für kommerzielle oder illegale Zwecke verwendet werden, andernfalls tragen die Nutzer alle Konsequenzen. Die Informationen auf dieser Seite stammen aus dem Internet, und Urheberrechtsstreitigkeiten haben nichts mit dieser Seite zu tun. Sie müssen die oben genannten Inhalte innerhalb von 24 Stunden nach dem Download vollständig von Ihrem Computer löschen. Wenn Ihnen das Programm gefällt, unterstützen Sie bitte echte Software, kaufen Sie die Registrierung und erhalten Sie bessere echte Dienstleistungen. Falls es eine Verletzung gibt, kontaktieren Sie uns bitte per E-Mail.

Mail To:help@itsvse.com