No WinForms, por que você não pode atualizar os controles da interface do usuário de outros threads?

StackOverflow https://stackoverflow.com/questions/10349

  •  08-06-2019
  •  | 
  •  

Pergunta

Tenho certeza de que há uma boa (ou pelo menos decente) razão para isso.O que é?

Foi útil?

Solução

Porque você pode facilmente acabar em um impasse (entre outros problemas).

Por exemplo, seu thread secundário pode estar tentando atualizar o controle da UI, mas o controle da UI estará aguardando a liberação de um recurso bloqueado pelo thread secundário, então ambos os threads acabam aguardando a conclusão um do outro.Como outros comentaram, esta situação não é exclusiva do código da UI, mas é particularmente comum.

Em outras linguagens, como C++, você pode tentar fazer isso (sem lançar uma exceção como no WinForms), mas seu aplicativo pode congelar e parar de responder caso ocorra um conflito.

Aliás, você pode facilmente informar ao thread da interface do usuário que deseja atualizar um controle, basta criar um delegado e chamar o método BeginInvoke (assíncrono) nesse controle, passando-o para seu delegado.Por exemplo.

myControl.BeginInvoke(myControl.UpdateFunction);

Isso é equivalente a fazer uma PostMessage C++/MFC a partir de um thread de trabalho

Outras dicas

Eu acho que esta é uma pergunta brilhante - e acho que há necessidade de uma resposta melhor.

Certamente a única razão é que há algo em uma estrutura em algum lugar que não é muito seguro para fios.

Esse "algo" é quase todos os membros da instância em todos os controles em System.Windows.Forms.

A documentação do MSDN para muitos controles em System.Windows.Forms, se não todos eles, digamos "Qualquer membro público estático (compartilhado no Visual Basic) desse tipo é thread-safe.Não há garantia de que quaisquer membros da instância sejam thread-safe."

Isso significa que membros da instância como TextBox.Text {get; set;} não são reentrante.

Tornar cada um desses membros da instância thread-safe pode introduzir muita sobrecarga que a maioria dos aplicativos não precisa.Em vez disso, os projetistas da estrutura .Net decidiram, e creio que corretamente, que o fardo de sincronizar o acesso aos controles de formulários de vários threads deveria ser colocado sobre o programador.

[Editar]

Embora esta pergunta pergunte apenas "por que", aqui está um link para um artigo que explica "como":

Como:Faça chamadas thread-safe para controles do Windows Forms no MSDN

http://msdn.microsoft.com/en-us/library/ms171728.aspx

Embora pareça razoável, a resposta de Johns não está correta.Na verdade, mesmo ao usar o Invoke, você ainda não está seguro de não se deparar com situações de impasse.Ao lidar com eventos disparados em um thread em segundo plano, usar Invoke pode até levar a esse problema.


O verdadeiro motivo tem mais a ver com as condições de corrida e remonta aos tempos antigos do Win32.Não posso explicar os detalhes aqui, as palavras-chave são bombas de mensagens, eventos WM_PAINT e as diferenças sutis entre "SEND" e "POST".


Informações adicionais podem ser encontradas aqui aqui e aqui.

Na versão 1.0/1.1, nenhuma exceção foi lançada durante a depuração; o que você obteve foi um cenário de interrupção intermitente do tempo de execução.Legal!:) Portanto, com 2.0, eles fizeram esse cenário lançar uma exceção e com razão.

A verdadeira razão para isso é provavelmente (como afirma Adam Haile) algum tipo de problema de simultaneidade/bloqueio.Observe que a API .NET normal (como TextBox.Text = "Hello";) envolve comandos SEND (que requerem ação imediata) que podem criar problemas se executados em um thread separado daquele que executa a atualização.Usar Invoke/BeginInvoke usa um POST que enfileira a ação.

Mais informações sobre ENVIAR e POST aqui.

É para que você não tenha duas coisas tentando atualizar o controle ao mesmo tempo.(Isso pode acontecer se a CPU mudar para o outro thread no meio de uma gravação/leitura) Mesmo motivo pelo qual você precisa usar mutexes (ou alguma outra sincronização) ao acessar variáveis ​​compartilhadas entre vários threads.

Editar:

Em outros idiomas, como C ++, você é livre para tentar fazer isso (sem uma exceção sendo jogada como em Winforms), mas acabará aprendendo da maneira mais difícil!

Ahh, sim... eu alterno entre C/C++ e C# e, portanto, era um pouco mais genérico do que deveria, desculpe...Ele está certo, você pode faça isso em C/C++, mas ele voltará para te morder!

Haveria também a necessidade de implementar a sincronização nas funções de atualização que são sensíveis a serem chamadas simultaneamente.Fazer isso para elementos de UI seria caro tanto no nível do aplicativo quanto no sistema operacional, e completamente redundante para a grande maioria do código.

Algumas APIs fornecem uma maneira de alterar a propriedade atual do thread de um sistema para que você possa atualizar temporariamente (ou permanentemente) sistemas de outros threads sem precisar recorrer à comunicação entre threads.

Hmm, não tenho certeza, mas acho que quando temos controles de progresso, como barras de espera, barras de progresso, podemos atualizar seus valores de outro thread e tudo funciona muito bem, sem falhas.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top