Эта статья является зеркальной статьёй машинного перевода, пожалуйста, нажмите здесь, чтобы перейти к оригиналу.

Вид: 6277|Ответ: 2

[Связь] [Поворот] Предложение. Задача. Результат будет заблокирован, как ещё написать этот код?

[Скопировать ссылку]
Опубликовано 7.08.2022 21:11:58 | | | |
1: Предыстория

1. Рассказывайте истории

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



Вероятно, это значит, что выполнение Task.Result в основной ветке Winform приведёт к тупику, я также посмотрел ссылку на ссылку на фото, Стивен — абсолютный босс, но эта статья в основном посвящена индоктринации большого абзаца текста, и она не даёт увидеть то, что видишь ты, поэтому я проанализирую это с точки зрения windbg.

2: Анализ Windbg

1. Действительно ли он окажется в тупике?

Конечно, я не играл в winform много лет и не могу понять, будет ли это, по крайней мере, не на консоли.

Код очень простой — запустить программу, клик-клик, и действительно, интерфейс застрял, что довольно невероятно.

2. Искать причину тупика

Далее поторопитесь и прикрепите виндбг к процессу, чтобы узнать.

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 (Boolean)
000004d10dfe250 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)
000004d10dfe2f0 00007ffb3a027b83 System.Windows.Forms.Button.OnClick(System.EventArgs)
000004d10dfe340 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)
000004d10dfe5f0 00007ffb3a030082 System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)
0000004d10dfe690 00007ffb3a765a02 DomainBoundILStubClass.IL_STUB_ReversePInvoke(Int64, Int32, Int64, Int64)
000004d10dfe9d0 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)
000004d10dfebf0 00007ffb3a045dc2 System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
000004d10dfec50 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:
    000004d10dfe250 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 000001f1f1f1b01200 2026020 Превентивный 000001F1800E70E8:000001F1800E7FD0 000001f1f1ad5b90 0 STA
   2 2 712c 000001f1f1b2a270 2b220 Превентивный 000000000000000000000000000000000000 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