Αυτό το άρθρο είναι ένα άρθρο καθρέφτη της αυτόματης μετάφρασης, κάντε κλικ εδώ για να μεταβείτε στο αρχικό άρθρο.

Άποψη: 6277|Απάντηση: 2

[Επικοινωνία] [Στροφή] Μια πρόταση Εργασία.Αποτέλεσμα θα κλειδωθεί, πώς αλλιώς να γράψετε αυτόν τον κώδικα;

[Αντιγραφή συνδέσμου]
Δημοσιεύτηκε στις 2022-8-7 21:11:58 | | | |
1: Ιστορικό

1. Πείτε ιστορίες

Πριν από λίγες μέρες, όταν ευρετηριάστηκα το άρθρο για το .NET Advanced Debugging στο github, βρήκα ένα ενδιαφέρον σχόλιο, δείτε το άρθρο για λεπτομέρειες, το στιγμιότυπο οθόνης έχει ως εξής:



Μάλλον σημαίνει ότι η εκτέλεση του Task.Result κάτω από το κύριο νήμα του Winform θα προκαλέσει αδιέξοδο, κοίταξα επίσης τον σύνδεσμο αναφοράς στην εικόνα, ο Stephen είναι το απόλυτο αφεντικό, αλλά αυτό το άρθρο αφορά κυρίως την κατήχηση μιας μεγάλης παραγράφου κειμένου και δεν σας αφήνει πραγματικά να δείτε αυτό που βλέπετε, οπότε θα το αναλύσω από την οπτική γωνία του windbg.

2: Ανάλυση Windbg

1. Θα είναι πραγματικά αδιέξοδο;

Φυσικά, δεν έχω παίξει winform εδώ και πολλά χρόνια και δεν μπορώ να καταλάβω αν θα γίνει, τουλάχιστον όχι στην κονσόλα.

Ο κώδικας είναι πολύ απλός, εκτελέστε το πρόγραμμα, κάντε κλικ στο κλικ και σίγουρα, η διεπαφή έχει κολλήσει, κάτι που είναι λίγο απίστευτο.

2. Αναζητήστε την αιτία του αδιεξόδου

Στη συνέχεια, βιαστείτε και συνδέστε το windbg στη διαδικασία για να μάθετε.

1) Κοιτάξτε το κύριο νήμα
Η διεπαφή δεν ανταποκρίνεται, επομένως φυσικά το κύριο νήμα έχει κολλήσει, επομένως πρέπει να ρίξετε μια ματιά στο τι κάνει το κύριο νήμα αυτήν τη στιγμή. Χρησιμοποιήστε την εντολή ~0s + !clrstack.

0:000> !clrstack
Αναγνωριστικό νήματος λειτουργικού συστήματος: 0x5a10 (0)
        Θυγατρική τοποθεσία κλήσης IP SP
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(Δυαδική τιμή)
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]
Από την έξοδο στοίβας, το κύριο νήμα έχει τελικά κολλήσει στο Monitor.ObjWait κάτω από το Task.Result, πράγμα που σημαίνει ότι δεν έχει πάρει ακόμα το τελευταίο jsonString, το οποίο είναι πολύ περίεργο, έχουν περάσει αρκετά λεπτά, υπάρχει πρόβλημα με το δίκτυο; Το δίχτυ μου είναι 100 μέτρα γεμάτο δύναμη πυρός...

2) Πού πήγε το jsonString;
Εάν το jsonString βρεθεί στον διαχειριζόμενο σωρό, σημαίνει ότι υπάρχει κάτι στο πρόγραμμα που κάνει το αποτέλεσμα να καθυστερεί, χρησιμοποιήστε την εντολή !dumpheap -type String -min 8500 + !do 000001f19002fcf0 για προβολή, όπως φαίνεται στο παρακάτω σχήμα:



Από το σχήμα, φαίνεται ξεκάθαρα ότι το html επέστρεψε, αφού όλα επέστρεψαν, γιατί δεν έχει τελειώσει ακόμα το Task.Result; Το επόμενο βήμα είναι να δείτε ποιος κατέχει αυτό το html και να χρησιμοποιήσετε το !gcroot.

0:000> !gcroot 000001f19002fcf0
Νήμα 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 Σύστημα χειρισμού συμβάντων
            -> 000001f1800716b0 System.Windows.Forms.ApplicationContext
            -> 000001f180071780 Σύστημα χειρισμού συμβάντων
            -> 000001f18006ab38 System.Windows.Forms.Application+ThreadContext
            -> 000001f18006b140 System.Windows.Forms.Application+MarshalingControl
            -> 000001f18016c9c8 Σύστημα.Συλλογές.Ουρά
            -> 000001f18016ca00 System.Object[]
            -> 000001f18016c948 System.Windows.Forms.Control+ThreadMethodEntry
            -> 000001f18016c8b8 System.Object[]
            -> 000001f1800e6f80 Σύστημα.Δράση
            -> 000001f1800e6f60 System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner
            -> 000001f1800a77d0 WindowsFormsApp4.Form1+<GetJsonAsync>d__2
            -> 000001f1800b4e50 System.Threading.Tasks.Task'1[[System.String, mscorlib]]
            -> 000001f19002fcf0 System.String

Βρέθηκαν 1 μοναδικές ρίζες (τρέξτε '! GCRoot -all' για να δείτε όλες τις ρίζες).
Από τα αποτελέσματα εξόδου, αυτό το System.String διατηρείται τελικά από το WindowsFormsApp4.Form1 του νήματος 5a10 και μπορείτε να χρησιμοποιήσετε το !t για να επαληθεύσετε τι είναι το νήμα 5a10.

0:000> !t Κλειδαριά  
       Αναγνωριστικό OSID ThreadOBJ Κατάσταση GC Λειτουργία GC Alloc Context Domain Count Apt Exception
   0 1 5a10 000001f1f1b01200 2026020 Προληπτικό 000001F1800E70E8:000001F1800E7FD0 000001f1f1ad5b90 0 STA
   2 2 712c 000001f1f1b2a270 2b220 Προληπτικό 000000000000000000000:000000000000000000 0000001f1f1ad5b90 0 MTA (Finalizer)
Πάω, το 5a10 αποδείχθηκε ότι ήταν το κύριο νήμα, ήταν πραγματικά λίγο μπερδεμένο, το κύριο νήμα είχε κολλήσει και η χορδή κρατιόταν από το κύριο νήμα, εντελώς ανεξήγητο.

3) Αναζητήστε σημεία ξεμπλοκαρίσματος
Εξακολουθώντας να επιστρέφω και να σκέφτομαι ήρεμα αυτήν την αλυσίδα αναφοράς, διαπίστωσα ότι υπάρχει μια ουρά εδώ: -> 000001f18016c9c8 System.Collections.Queue, έχω μια ιδέα, μπορώ να διορθώσω τον πηγαίο κώδικα τοποθετώντας ένα σημείο διακοπής στην ουρά και το εργαλείο χρησιμοποιεί το DnSpy, απλά κάντε το.



Όπως μπορείτε να δείτε από το σχήμα, κατά την είσοδο στην ουρά, χρησιμοποιείται το νήμα 10, πράγμα που σημαίνει ότι η συμβολοσειρά δεν συγκρατείται ακόμη από το κύριο νήμα αυτήν τη στιγμή.



Από το διάγραμμα, μπορεί να φανεί ότι η συνεχής εργασία προγραμματίζεται τελικά από WindowsFormsSynchronizationContext.Post στην ουρά υπό έλεγχο και τα δεδομένα σε αυτήν την ουρά πρέπει να εκτελεστούν από το νήμα διεπαφής χρήστη, επομένως υπάρχει ο ακόλουθος διάλογος:

Κύριο νήμα: Task αδερφέ, πότε θα ολοκληρώσεις την εκτέλεσή του; Περιμένω να ολοκληρώσεις το σήμα;

αγγαρεία:Αδερφέ, αν δεν με εκτελέσεις, πώς μπορώ να το τελειώσω;

Κύριο νήμα:Ω...

Συνοπτικά: η εργασία συνέχισης έχει φτάσει στην ουρά περιμένοντας να εκτελεστεί το κύριο νήμα και αυτή τη στιγμή, το κύριο νήμα είναι αναισθητοποιημένο και περιμένει να ολοκληρωθεί η εργασία συνέχειας=true, το πρόβλημα είναι εδώ: πώς μπορεί να μην εκτελεστεί η εργασία συνέχειας Complete=true; Έτσι είναι λοιπόν.

Τρία: Πώς να το σπάσετε

Γνωρίζοντας την αιτία και το αποτέλεσμα, αυτή η μέθοδος πυρόλυσης είναι απλή, χωρίζεται χονδρικά σε δύο τύπους.

1. Απαγορεύεται η απόθεση μιας εργασίας συνέχειας σε μια ουρά

Για να κόψετε αυτήν τη διαδρομή, το συμπέρασμα είναι να αφήσετε τη δεξαμενή νημάτων να τερματίσει την εργασία από μόνη της, έτσι ώστε το νήμα διεπαφής χρήστη να μπορεί να αντιληφθεί ότι η εργασία έχει ολοκληρωθεί και, τέλος, το νήμα διεπαφής χρήστη να μπορεί να λάβει το τελικό html, το οποίο είναι να προσθέσετε το ConfigureAwait(false) μετά την αναμονή, ως εξής:



2. Μπλοκάρετε το κύριο νήμα

Εάν το κύριο νήμα δεν είναι αποκλεισμένο, τότε το κύριο νήμα μπορεί ελεύθερα να αποκτήσει τις εργασίες που πρέπει να εκτελεστούν στο Control.Queue και η μέθοδος είναι πολύ απλή, απλώς προσθέστε την αναμονή πριν από το GetJsonAsync.



Τρία: Περίληψη

Το συμπέρασμα είναι να κάνεις περισσότερη από τη δική σου πρακτική άσκηση, η θεωρητική γνώση σου ενσταλάσσεται με το ζόρι από τους άλλους, είτε είναι σωστή είτε λάθος, στην πραγματικότητα, δεν έχεις πάτο στην καρδιά σου, η πρακτική επαλήθευση είναι αυτό που πραγματικά σου ανήκει και είναι δύσκολο να ξεχάσεις, τελικά, έχεις πραγματικά βιώσει, εξασκηθεί και επαληθεύσει.

Αρχικός:Η σύνδεση με υπερσύνδεσμο είναι ορατή.





Προηγούμενος:Το Docker τροποποιεί και περιορίζει την CPU, τη μνήμη και άλλους πόρους του κοντέινερ
Επόμενος:Οι πίνακες JS είναι η διαφορά και η χρήση του καθενός και μερικών
Δημοσιεύτηκε στις 2022-8-8 17:01:32 |
Μάθε να μαθαίνεις...
Δημοσιεύτηκε στις 2022-9-2 11:39:19 |
Μάθετε λίγα
Αποκήρυξη:
Όλο το λογισμικό, το υλικό προγραμματισμού ή τα άρθρα που δημοσιεύονται από το Code Farmer Network προορίζονται μόνο για μαθησιακούς και ερευνητικούς σκοπούς. Το παραπάνω περιεχόμενο δεν θα χρησιμοποιηθεί για εμπορικούς ή παράνομους σκοπούς, άλλως οι χρήστες θα υποστούν όλες τις συνέπειες. Οι πληροφορίες σε αυτόν τον ιστότοπο προέρχονται από το Διαδίκτυο και οι διαφορές πνευματικών δικαιωμάτων δεν έχουν καμία σχέση με αυτόν τον ιστότοπο. Πρέπει να διαγράψετε εντελώς το παραπάνω περιεχόμενο από τον υπολογιστή σας εντός 24 ωρών από τη λήψη. Εάν σας αρέσει το πρόγραμμα, υποστηρίξτε γνήσιο λογισμικό, αγοράστε εγγραφή και λάβετε καλύτερες γνήσιες υπηρεσίες. Εάν υπάρχει οποιαδήποτε παραβίαση, επικοινωνήστε μαζί μας μέσω email.

Mail To:help@itsvse.com