Ця стаття є дзеркальною статтею машинного перекладу, будь ласка, натисніть тут, щоб перейти до оригінальної статті.

Вид: 6277|Відповідь: 2

[Зв'язок] [Поворот] Речення Завдання. Результат буде заблоковано, як інакше написати цей код?

[Копіювати посилання]
Опубліковано 2022-8-7 21:11:58 | | | |
1: Передумови

1. Розповідати історії

Кілька днів тому, коли я індексував статтю про .NET Advanced Debugging на github, я знайшов цікавий коментар, деталі дивіться статтю, скріншот виглядає так:



Ймовірно, це означає, що виконання Task.Result у основній гілці Winform призведе до глухого кута, я також переглянув посилання на зображення, Стівен — абсолютний бос, але ця стаття здебільшого про індоктринацію великого абзацу тексту, і вона не дає побачити те, що бачиш ти, тому я проаналізую це з точки зору 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]
000004d10dfe2b0 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)
000004d10dfe400 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 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 Система. Об'єкт[]
            -> 000001f18016c948 System.Windows.Forms.Control+ThreadMethodEntry
            -> 000001f18016c8b8 Система. Об'єкт[]
            -> 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> Замок  
       ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
   0 1 5a10 000001f1f1b01200 2026020 Превентивний 000001F1800E70E8:000001F1800E7FD0 000001f1f1ad5b90 0 STA
   2 2 712c 000001f1f1b2a270 2b220 Преемптивний 0000000000000000000000000000000000000 000000001f1f1ad5b90 0 MTA (Фіналізатор)
Я йду, 5a10 виявився основною ниткою, це було трохи заплутано, основна нитка застрягла, а нитка трималася за основну нитку, що було абсолютно незрозуміло.

3) Шукайте точки прориву
Все ще повертаючись і спокійно розмірковуючи над цим посиланням, я виявив, що тут є черга: -> 000001f18016c9c8 System.Collections.Queue, у мене є ідея, я можу налагодити вихідний код, розмістивши точку зупину в черзі, і інструмент використовує DnSpy, просто зроби це.



Як видно на рисунку, при вході в чергу використовується потік 10, що означає, що рядок ще не утримується основним потоком у цей момент.



З діаграми видно, що безперервне завдання нарешті планується WindowsFormsSynchronizationContext.Post в черзі під контролем, і дані в цій черзі потрібно виконати через потік інтерфейсу, тому є наступний діалог:

Головна тема: Завдання, брате, коли ти закінчиш його виконання? Я чекаю, поки ти завершиш сигнал?

завдання:Брате, якщо ти мене не стратиш, як я це закінчу?

Основна тема:Ах...

Підсумовуючи: завдання продовження досягло черги, що чекає на виконання основного потоку, і в цей момент основний потік перебуває в глухому стані та чекає завершення завдання продовження =true, проблема в тому, як завдання продовження не може бути виконане Complete=true? Отже, вони ось такі.

Третє: як його розгадати

Знаючи причину і наслідок, цей метод тріщини простий і приблизно поділяється на два типи.

1. Заборонено ставити завдання продовження у чергу

Щоб відрізати цей шлях, слід дозволити пулу потоку завершити завдання самостійно, щоб потік UI міг відчути, що завдання виконано, і нарешті потік UI отримав фінальний html, тобто додати ConfigureAwait(false) після await, наступним чином:



2. Заблокувати основну нитку

Якщо основний потік не заблокований, тоді основний потік може вільно отримувати завдання, які потрібно виконати в Control.Queue, і метод дуже простий — просто додайте waitit перед GetJsonAsync.



Третє: Підсумок

Висновок — займатися власною практичною практикою, теоретичні знання нав'язують тобі інші, незалежно від того, правильні вони чи ні, насправді у тебе немає дна в серці, практична перевірка — це те, що справді належить тобі, і важко забути, адже ти справді пережив, практикував і перевіряв.

Оригінальний:Вхід за гіперпосиланням видно.





Попередній:Docker модифікує та обмежує контейнерний процесор, пам'ять та інші ресурси
Наступний:JS-масиви — це різниця та використання кожного з них
Опубліковано 2022-8-8 17:01:32 |
Навчися вчитися...
Опубліковано 2022-9-2 11:39:19 |
Навчися трохи
Застереження:
Усе програмне забезпечення, програмні матеріали або статті, опубліковані Code Farmer Network, призначені лише для навчання та досліджень; Вищезазначений контент не повинен використовуватися в комерційних чи незаконних цілях, інакше користувачі несуть усі наслідки. Інформація на цьому сайті надходить з Інтернету, і спори щодо авторських прав не мають до цього сайту. Ви повинні повністю видалити вищезазначений контент зі свого комп'ютера протягом 24 годин після завантаження. Якщо вам подобається програма, будь ласка, підтримуйте справжнє програмне забезпечення, купуйте реєстрацію та отримайте кращі справжні послуги. Якщо є будь-яке порушення, будь ласка, зв'яжіться з нами електронною поштою.

Mail To:help@itsvse.com