Este artigo é um artigo espelhado de tradução automática, por favor clique aqui para ir para o artigo original.

Vista: 6277|Resposta: 2

[Comunicação] [Turn] Uma frase Tarefa. O resultado será bloqueado, como mais escrever esse código?

[Copiar link]
Postado em 2022-8-7 21:11:58 | | | |
1: Contexto

1. Contar histórias

Alguns dias atrás, quando indexei o artigo sobre o Depuração Avançada do .NET no github, encontrei um comentário interessante, veja o artigo para detalhes, a captura de tela é a seguinte:



Provavelmente significa que executar o Task.Result sob o tópico principal do Winform causará um impasse, também olhei o link de referência na imagem, Stephen é o chefe absoluto, mas este artigo é principalmente sobre a doutrinação de um grande parágrafo de texto, e não permite realmente ver o que você vê, então vou analisar do ponto de vista do windbg.

2: Análise de Windbg

1. Será que realmente vai ficar empatado?

Claro, não jogo winform há muitos anos, e não consigo saber se vai jogar assim, pelo menos não no console.

O código é muito simples, roda o programa, clica clica, e claro, a interface trava, o que é meio incrível.

2. Procurar a causa do impasse

Em seguida, apresse-se e anexe windbg ao processo para descobrir.

1) Olhar para o tópico principal
A interface não responde, então naturalmente a thread principal está travada, então você precisa olhar o que a thread principal está fazendo neste momento. Use o comando ~0s + !clrstack.

0:000> !clrstack
ID da Thread do OS: 0x5a10 (0)
        Site de Chamadas IP Child 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 (Booleano)
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]
A partir da saída da pilha, a thread principal finalmente fica travada no Monitor.ObjWait em Task.Result, o que significa que ainda não pegou a última jsonString, o que é muito estranho, já se passaram vários minutos, há algum problema com a rede? Minha rede está cheia de 100M de poder de fogo...

2) Para onde foi parar o jsonString?
Se jsonString for encontrado no heap gerenciado, significa que há algo no programa que faz o resultado atrasar, use o comando !dumpheap -type String -min 8500 + !do 000001f19002fcf0 para visualizar, como mostrado na figura abaixo:



Pela figura, dá para ver claramente que o html voltou, já que está tudo de volta, por que o Task.Result ainda não terminou? O próximo passo é ver quem detém esse html e usar !gcroot.

0:000> !gcroot 000001F19002fcf0
Tópico 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 Sistema.EventHandler
            -> 000001f1800716b0 System.Windows.Forms.ApplicationContext
            -> 000001f180071780 Sistema.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 Sistema. Ação
            -> 000001f1800e6f60 System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner
            -> 000001f1800a77d0 WindowsFormsApp4.Form1+<GetJsonAsync>d__2
            -> 000001f1800b4e50 System.Threading.Tasks.Task'1[[System.String, mscorlib]]
            -> 000001f19002fcf0 System.String

Encontrei 1 raiz única (corre '! GCRoot -todos' para ver todas as raízes).
Pelos resultados de saída, este System.String é finalmente mantido por WindowsFormsApp4.Form1 do thread 5a10, e você pode usar !t para verificar qual thread é 5a10.

0:000> !t Tranca  
       ID OSID ThreadOBJ State GC Mode GC Alloc Contexto Contagem de Domínio Apt Exception
   0 1 5a10 000001f1f1b01200 2026020 Preemptivo 000001F1800E70E8:000001F1800E7FD0 000001f1f1ad5b90 0 STA
   2 2 712c 000001f1f1b2a270 2b220 Preemptivo 0000000000000000:000000000000000000000 000001f1f1f1ad5b90 0 MTA (Finalizer)
Eu digo, 5a10 acabou sendo o fio principal, foi um pouco confuso, o fio principal estava preso e o fio estava preso pelo fio principal, completamente inexplicável.

3) Procurar pontos de ruptura
Ainda pensando calmamente nessa cadeia de referência, descobri que existe uma fila aqui: -> 000001f18016c9c8 System.Collections.Queue, tenho uma ideia, posso depurar o código-fonte colocando um ponto de quebra na fila, e a ferramenta usa DnSpy, é só fazer isso.



Como você pode ver na figura, ao entrar na Fila, a thread 10 é usada, o que significa que a string ainda não está mantida pela thread principal neste momento.



Pelo diagrama, pode-se ver que a tarefa contínua é finalmente agendada por WindowsFormsSynchronizationContext.Post na Fila sob Controle, e os dados dessa Fila precisam ser executados pela thread de interface, então há o seguinte diálogo:

Tópico principal: Tarefa, irmão, quando você termina de executá-la? Estou esperando você completar o sinal?

tarefa:Irmão, se você não me executar, como vou acabar com isso?

Tópico principal:Ah...

Resumindo: a tarefa de continuação chegou à fila esperando a execução da thread principal, e nesse momento, a thread principal está atordoada e está esperando a tarefa de continuação ser concluída=true, o problema é aqui: como a tarefa de continuação não pode ser executada Complete=true? Então eles são assim.

Três: Como desvendá-lo

Sabendo a causa e efeito, esse método de trinca é simples, dividido grosso modo em dois tipos.

1. É proibido inserir uma tarefa de continuação em uma fila

Para cortar esse caminho, a implicação é deixar o pool de threads encerrar a tarefa sozinho, para que o thread da interface possa sentir que a tarefa foi concluída, e finalmente o thread da interface possa receber o html final, que é adicionar ConfigureAwait(false) após await, da seguinte forma:



2. Bloquear o fio principal

Se a thread principal não estiver bloqueada, então a thread principal pode obter livremente as tarefas que precisam ser executadas no Control.Queue, e o método é muito simples: basta adicionar await antes do GetJsonAsync.



Três: Resumo

A conclusão é que faça mais da sua própria prática prática, o conhecimento teórico é forçado a ser incutido por outros, seja certo ou errado, na verdade, você não tem fundo no coração, a verificação prática é o que realmente pertence a você, e é difícil esquecer, afinal, que você realmente experimentou, praticou e verificou.

Original:O login do hiperlink está visível.





Anterior:Docker modifica e restringe CPU de container, memória e outros recursos
Próximo:Os arrays JS são a diferença e o uso de todos e alguns
Postado em 2022-8-8 17:01:32 |
Aprenda a aprender...
Postado em 2022-9-2 11:39:19 |
Aprenda um pouco
Disclaimer:
Todo software, material de programação ou artigos publicados pela Code Farmer Network são apenas para fins de aprendizado e pesquisa; O conteúdo acima não deve ser usado para fins comerciais ou ilegais, caso contrário, os usuários terão todas as consequências. As informações deste site vêm da Internet, e disputas de direitos autorais não têm nada a ver com este site. Você deve deletar completamente o conteúdo acima do seu computador em até 24 horas após o download. Se você gosta do programa, por favor, apoie um software genuíno, compre o registro e obtenha serviços genuínos melhores. Se houver qualquer infração, por favor, entre em contato conosco por e-mail.

Mail To:help@itsvse.com