Ten artykuł jest lustrzanym artykułem tłumaczenia maszynowego, kliknij tutaj, aby przejść do oryginalnego artykułu.

Widok: 6277|Odpowiedź: 2

[Komunikacja] [Turn] Zadanie zdania. Wynik zostanie zablokowany, jak inaczej napisać ten kod?

[Skopiuj link]
Opublikowano 2022-8-7 21:11:58 | | | |
1: Tło

1. Opowiadaj historie

Kilka dni temu, gdy indeksowałem artykuł o .NET Advanced Debuging na githubie, znalazłem ciekawy komentarz, szczegóły znajdziesz w artykule – zrzut ekranu jest następujący:



Prawdopodobnie oznacza to, że wykonanie Task.Result w głównym wątku Winform spowoduje impas. Też spojrzałem na link referencyjny na zdjęciu, Stephen to absolutny boss, ale ten artykuł dotyczy głównie indoktrynacji długiego akapitu tekstu i nie pozwala zobaczyć tego, co widzisz, więc przeanalizuję to z perspektywy windbg.

2: Analiza Windbg

1. Czy naprawdę będzie impasem?

Oczywiście nie grałem w Winform od wielu lat i nie mogę się dowiedzieć, czy to się stanie, przynajmniej nie na konsoli.

Kod jest bardzo prosty: uruchamiasz program, klikasz klik i rzeczywiście, interfejs się zacina, co jest naprawdę niesamowite.

2. Szukaj przyczyny impasu

Następnie szybko dołącz windbg do procesu, żeby się przekonać.

1) Spójrz na główny wątek
Interfejs jest nieresponsywny, więc naturalnie główny wątek utknie, więc musisz sprawdzić, co robi główny wątek w tym momencie. Użyj polecenia ~0s + !clrstack.

0:000> !clrstack
ID wątku systemu: 0x5a10 (0)
        Child SP               IP Call Site
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 wyjścia stosu główny wątek w końcu utknął na Monitor.ObjWait pod Task.Result, co oznacza, że nie pobrano jeszcze ostatniego jsonString, co jest bardzo dziwne, minęło już kilka minut, czy jest problem z siecią? Moja sieć to 100M pełna siły ognia...

2) Gdzie podział się jsonString?
Jeśli jsonString znajduje się na zarządzanym stercie, oznacza to, że w programie jest coś, co powoduje opóźnienie wyniku, użyj polecenia !dumpheap -type String -min 8500 + !do 000001f19002fcf0, aby zobaczyć, jak pokazano na poniższym rysunku:



Na rysunku wyraźnie widać, że html wrócił, skoro wszystko wróciło, to dlaczego Task.Result jeszcze się nie zakończył? Kolejnym krokiem jest sprawdzenie, kto posiada ten html i użycie !gcroot.

0:000> !gcroot 000001f19002fcf0
Wątek 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 System.Object[]
            -> 000001f18016c948 System.Windows.Forms.Control+ThreadMethodEntry
            -> 000001f18016c8b8 System.Object[]
            -> 000001f1800e6f80 System. Akcja
            -> 000001f1800e6f60 System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner
            -> 000001f1800a77d0 WindowsFormsApp4.Form1+<GetJsonAsync>d__2
            -> 000001f1800b4e50 System.Threading.Tasks.Task'1[[System.String, mscorlib]]
            -> 000001f19002fcf0 System.String

Znalazłem 1 unikalne korzenie (uciekaj! GCRoot -all' dla zobaczenia wszystkich korzeni).
Z wyników wyjściowych wynika, że ten System.String jest ostatecznie przechowywany przez WindowsFormsApp4.Form1 wątku 5a10 i możesz użyć !t, aby zweryfikować, czym jest wątek 5a10.

0:000> !t Lock  
       ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
   0 1 5a10 0000001f1f1b01200 2026020 Preemptive 000001F1800E70E8:000001F1800E7FD0 000001f1f1ad5b90 0 STA
   2 2 712c 0000001f1f1f1b2a270 2b220 Preemptive 000000000000000000:000000000000000000000000000 000001f1f1ad5b90 0 MTA (Finalizer)
Powiedziałem, że 5a10 okazało się głównym nicią, to było naprawdę trochę mylące, główny głód się zaciął, a struna była trzymana przez główną, całkowicie niewytłumaczalne.

3) Szukaj punktów przełamania
Wciąż wracając i spokojnie myśląc o tym łańcuchu odniesień, zauważyłem, że jest tu kolejka: -> 000001f18016c9c8 System.Collections.Queue, mam pomysł, mogę debugować kod źródłowy, umieszczając punkt przerwania w kolejce, a narzędzie używa DnSpy, po prostu to zrób.



Jak widać na rysunku, przy wejściu do kolejki używany jest wątek 10, co oznacza, że ciąg nie jest jeszcze zatrzymany przez główny wątek w tym momencie.



Z diagramu widać, że zadanie ciągłe jest ostatecznie zaplanowane przez WindowsFormsSynchronizationContext.Post do Kolejki pod Kontrolą, a dane w tej Kolejce muszą być wykonywane przez wątek UI, więc pojawia się następujący dialog:

Główny wątek: Zadanie, Bracie, kiedy skończysz je wykonać? Czekam, aż dokończysz sygnał?

zadanie:Bracie, jeśli mnie nie wykonasz, jak mam to skończyć?

Główny wątek:Och...

Podsumowując: zadanie kontynuacji dotarło do kolejki czekające na wykonanie głównego wątku, a w tym momencie główny wątek jest ogłuszony i czeka na zakończenie zadania kontynuacji =true, problem pojawia się tutaj: jak to możliwe, że zadanie kontynuacji nie zostanie wykonane Complete=true? Więc tak to wygląda.

Po trzecie: Jak to złamać

Znając przyczyny i skutek, ta metoda pękania jest prosta i w przybliżeniu dzieli się na dwa typy.

1. Zakazuje się wrzucania zadania kontynuacyjnego do kolejki

Aby odciąć tę ścieżkę, oznacza to, że pula wątków sama zakończy zadanie, tak aby wątek UI mógł wyczuć, że zadanie zostało ukończone, a na końcu wątek UI może otrzymać ostateczny html, czyli dodanie ConfigureAwait(false) po await, w następujący sposób:



2. Zablokuj główny wątek

Jeśli główny wątek nie jest zablokowany, główny wątek może swobodnie pobierać zadania wymagane w Control.Queue, a metoda jest bardzo prosta – wystarczy dodać await przed GetJsonAsync.



Trzecie: Podsumowanie

Wniosek jest taki, że jeśli więcej praktykujesz samodzielnie, wiedza teoretyczna jest ci wpajana przez innych, niezależnie od tego, czy jest słuszna, czy nie, w rzeczywistości nie masz dna w sercu, praktyczna weryfikacja jest tym, co naprawdę do ciebie należy, i trudno o tym zapomnieć, w końcu naprawdę doświadczyłeś, praktykowałeś i zweryfikowałeś.

Oryginał:Logowanie do linku jest widoczne.





Poprzedni:Docker modyfikuje i ogranicza CPU, pamięć i inne zasoby kontenerowe
Następny:Matryce JS to różnica i zastosowanie każdego z nich
Opublikowano 8.08.2022 o 17:01:32 |
Naucz się uczyć...
Opublikowano 2022-9-2 11:39:19 |
Ucz się trochę
Zrzeczenie się:
Całe oprogramowanie, materiały programistyczne lub artykuły publikowane przez Code Farmer Network służą wyłącznie celom edukacyjnym i badawczym; Powyższe treści nie mogą być wykorzystywane do celów komercyjnych ani nielegalnych, w przeciwnym razie użytkownicy ponoszą wszelkie konsekwencje. Informacje na tej stronie pochodzą z Internetu, a spory dotyczące praw autorskich nie mają z nią nic wspólnego. Musisz całkowicie usunąć powyższą zawartość z komputera w ciągu 24 godzin od pobrania. Jeśli spodoba Ci się program, wspieraj oryginalne oprogramowanie, kup rejestrację i korzystaj z lepszych, autentycznych usług. W przypadku naruszenia praw prosimy o kontakt mailowy.

Mail To:help@itsvse.com