Respostas:
Você quer dizer Delegate.Invoke
/ BeginInvoke
ou Control.Invoke
/ BeginInvoke
?
Delegate.Invoke
: Executa de forma síncrona, no mesmo encadeamento.Delegate.BeginInvoke
: Executa de forma assíncrona, em um threadpool
encadeamento.Control.Invoke
: É executado no thread da interface do usuário, mas o thread de chamada aguarda a conclusão antes de continuar.Control.BeginInvoke
: Executa no thread da interface do usuário e o thread de chamada não espera a conclusão.A resposta de Tim menciona quando você pode querer usar BeginInvoke
- embora tenha sido direcionado principalmente Delegate.BeginInvoke
, suspeito.
Para aplicativos do Windows Forms, sugiro que você use normalmenteBeginInvoke
. Dessa forma, você não precisa se preocupar com o impasse, por exemplo - mas precisa entender que a interface do usuário pode não ter sido atualizada até a próxima vez que você olhar para ela! Em particular, você não deve modificar os dados que o encadeamento da interface do usuário pode estar prestes a usar para fins de exibição. Por exemplo, se você possui um Person
com FirstName
e LastName
propriedades, e você fez:
person.FirstName = "Kevin"; // person is a shared reference
person.LastName = "Spacey";
control.BeginInvoke(UpdateName);
person.FirstName = "Keyser";
person.LastName = "Soze";
Então a interface do usuário pode acabar exibindo "Keyser Spacey". (Há uma chance externa de que ele possa exibir "Kevin Soze", mas apenas pela estranheza do modelo de memória.)
A menos que você tenha esse tipo de problema, no entanto, Control.BeginInvoke
é mais fácil acertar e evitará que o thread de segundo plano precise esperar sem um bom motivo. Observe que a equipe do Windows Forms garantiu que você pode usar de Control.BeginInvoke
maneira "disparar e esquecer" - ou seja, sem nunca ligar EndInvoke
. Isso não se aplica às chamadas assíncronas em geral: normalmente todo BeginXXX deve ter uma chamada EndXXX correspondente, geralmente no retorno de chamada.
Com base na resposta de Jon Skeet, há momentos em que você deseja chamar um delegado e aguardar sua execução antes que o encadeamento atual continue. Nesses casos, a chamada de chamada é o que você deseja.
Em aplicativos multiencadeamento, talvez você não queira que um thread aguarde um delegado para concluir a execução, especialmente se esse delegado executar E / S (o que poderia fazer com que o delegado e seu thread bloqueiem).
Nesses casos, o BeginInvoke seria útil. Ao chamá-lo, você está dizendo ao delegado para iniciar, mas seu segmento fica livre para fazer outras coisas em paralelo com o delegado.
O uso do BeginInvoke aumenta a complexidade do seu código, mas há momentos em que o desempenho aprimorado vale a complexidade.
A diferença entre Control.Invoke()
e Control.BeginInvoke()
é,
BeginInvoke()
agendará a ação assíncrona no encadeamento da GUI. Quando a ação assíncrona é agendada, seu código continua. Algum tempo depois (você não sabe exatamente quando) sua ação assíncrona será executadaInvoke()
executará sua ação assíncrona (no encadeamento da GUI) e esperará até que sua ação seja concluída.Uma conclusão lógica é que um delegado ao qual você passa Invoke()
pode ter parâmetros externos ou um valor de retorno, enquanto um delegado ao qual você passa BeginInvoke()
não pode (você precisa usar o EndInvoke para recuperar os resultados).
Apenas para dar um exemplo curto e prático para ver o efeito da diferença
new Thread(foo).Start();
private void foo()
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(ThreadStart)delegate()
{
myTextBox.Text = "bing";
Thread.Sleep(TimeSpan.FromSeconds(3));
});
MessageBox.Show("done");
}
Se você usar o BeginInvoke , o MessageBox será exibido simultaneamente à atualização de texto. Se usar Invoke , o MessageBox será exibido após os 3 segundos de suspensão. Portanto, mostrando o efeito de uma chamada assíncrona ( BeginInvoke ) e síncrona ( Invoke ).
Delegate.BeginInvoke () enfileira de forma assíncrona a chamada de um delegado e retorna o controle imediatamente. Ao usar Delegate.BeginInvoke (), você deve chamar Delegate.EndInvoke () no método de retorno de chamada para obter os resultados.
Delegate.Invoke () chama de forma síncrona o delegado no mesmo segmento.
Apenas adicionando por que e quando usar Invoke ().
Invoke () e BeginInvoke () empacotam o código que você especifica para o encadeamento do expedidor.
Mas, ao contrário de BeginInvoke (), Invoke () interrompe seu encadeamento até o expedidor executar seu código. Convém usar Invoke () se precisar pausar uma operação assíncrona até que o usuário forneça algum tipo de feedback.
Por exemplo, você pode chamar Invoke () para executar um trecho de código que mostra uma caixa de diálogo OK / Cancelar. Depois que o usuário clicar em um botão e seu código empacotado for concluído, o método invoke () retornará e você poderá agir de acordo com a resposta do usuário.
Consulte Pro WPF em C #, capítulo 31