Fiquei dolorosamente ciente da frequência com que é necessário escrever o seguinte padrão de código no código da GUI orientada a eventos, em que
private void DoGUISwitch() {
// cruisin for a bruisin' through exception city
object1.Visible = true;
object2.Visible = false;
}
torna-se:
private void DoGUISwitch() {
if (object1.InvokeRequired) {
object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));
} else {
object1.Visible = true;
object2.Visible = false;
}
}
Esse é um padrão estranho no C #, tanto para lembrar quanto para digitar. Alguém veio com algum tipo de atalho ou construção que automatiza isso até certo ponto? Seria legal se houvesse uma maneira de anexar uma função a objetos que fazem essa verificação sem ter que passar por todo esse trabalho extra, como um object1.InvokeIfNecessary.visible = true
atalho de tipo.
Respostas anteriores discutiram a impraticabilidade de apenas chamar Invoke () todas as vezes, e mesmo assim a sintaxe Invoke () é ineficiente e ainda difícil de lidar.
Então, alguém descobriu algum atalho?
c#
multithreading
winforms
thread-safety
invokerequired
Tom Corelis
fonte
fonte
object1.InvokeIfNecessary.Visible = true
fala; confira minha resposta atualizada e deixe-me saber o que você pensa.Respostas:
A abordagem de Lee pode ser simplificada ainda mais
E pode ser chamado assim
Não há necessidade de passar o controle como parâmetro para o delegado. C # cria automaticamente um fechamento .
ATUALIZAÇÃO :
De acordo com vários outros pôsteres,
Control
pode ser generalizado comoISynchronizeInvoke
:DonBoitnott apontou que, diferentemente
Control
daISynchronizeInvoke
interface, requer uma matriz de objetos para oInvoke
método como lista de parâmetros para oaction
.ATUALIZAÇÃO 2
Edições sugeridas por Mike de Klerk (veja o comentário no 1º trecho de código para inserir pontos):
Consulte o comentário do ToolmakerSteve abaixo para preocupações sobre esta sugestão.
fonte
ISynchronizeInvoke
vez deControl
? (Parabéns a Jon Skeet stackoverflow.com/questions/711408/… )ISynchronizeInvoke
. Mas o único tipo que deriva dele (de acordo com Refletor) éControl
, portanto, a vantagem é limitada.while (!control.Visible) ..sleep..
. Para mim, isso tem um cheiro ruim de código, pois é um atraso potencialmente ilimitado (talvez até um loop infinito em alguns casos), em código que pode ter chamadores que não esperam esse atraso (ou mesmo um impasse). IMHO, qualquer uso deSleep
deve ser de responsabilidade de cada chamador, OU deve estar em um invólucro separado que esteja claramente marcado quanto às suas consequências. IMHO, geralmente seria melhor "falhar bastante" (exceção, capturar durante o teste) ou "não fazer nada" se o controle não estiver pronto. Comentários?InvokeRequired
informa se o segmento de chamada é diferente do segmento que criou o controle.Invoke
passa a ação do thread de chamada para o thread do controle em que é executado. Isso garante que, por exemplo, um manipulador de eventos de clique nunca seja interrompido.Você pode escrever um método de extensão:
E use-o assim:
EDIT: Como Simpzon aponta nos comentários, você também pode alterar a assinatura para:
fonte
Aqui está o formulário que eu tenho usado em todo o meu código.
Baseei isso na entrada do blog aqui . Como essa abordagem não me falhou, não vejo motivo para complicar meu código com uma verificação da
InvokeRequired
propriedade.Espero que isto ajude.
fonte
InvokeRequired
se o código puder ser executado antes do controle ser mostrado ou você terá uma exceção fatal.Crie um arquivo ThreadSafeInvoke.snippet e selecione as instruções de atualização, clique com o botão direito do mouse e selecione 'Surround With ...' ou Ctrl-K + S:
fonte
Aqui está uma versão aprimorada / combinada das respostas de Lee, Oliver e Stephan.
O modelo permite um código flexível e sem conversão, que é muito mais legível enquanto o delegado dedicado fornece eficiência.
fonte
Prefiro usar uma única instância de um método Delegate em vez de criar uma nova instância sempre. No meu caso, eu costumava mostrar mensagens de progresso e (informações / erro) de um Backroundworker copiando e lançando grandes dados de uma instância sql. Depois de cerca de 70000 chamadas de progresso e mensagens, meu formulário parou de funcionar e de mostrar novas mensagens. Isso não ocorreu quando comecei a usar um único delegado de instância global.
fonte
Uso:
Código:
fonte
Eu meio que gosto de fazer um pouco diferente, eu gosto de me chamar de "eu mesmo" se necessário com uma ação,
este é um padrão útil, o IsFormClosing é um campo que eu defini como True quando estou fechando meu formulário, pois pode haver alguns threads de segundo plano que ainda estão em execução ...
fonte
Você nunca deve escrever código parecido com este:
Se você possui um código parecido com este, seu aplicativo não é seguro para threads. Isso significa que você tem um código que já está chamando DoGUISwitch () de um thread diferente. É muito tarde para verificar se está em um segmento diferente. InvokeRequire deve ser chamado ANTES de fazer uma chamada para DoGUISwitch. Você não deve acessar nenhum método ou propriedade de um thread diferente.
Referência: Control.InvokeRequired Propriedade onde você pode ler o seguinte:
Em uma arquitetura de CPU única, não há problema, mas em uma arquitetura de várias CPUs, você pode fazer com que parte do thread da UI seja atribuída ao processador em que o código de chamada estava em execução ... e se esse processador for diferente de onde o thread da UI estava em execução quando o segmento de chamada terminar, o Windows pensará que o segmento da interface do usuário terminou e interromperá o processo do aplicativo, ou seja, seu aplicativo será encerrado sem erros.
fonte
invoke()
et al antes que o controle seja tratado, mas o IMHO não descreve o que você descreveu. O objetivo de toda essainvoke()
bobagem é atualizar a interface do usuário de uma maneira segura para threads, e eu acho que colocar mais instruções em um contexto de bloqueio levaria à gagueira? (Ugh ... contente eu parei de usar M $ tecnologia tão complicada.!)