Qual é a diferença entre Invoke () e BeginInvoke ()

398

Imaginando qual a diferença entre BeginInvoke()e Invoke()são?

Principalmente para que cada um seria usado.

EDIT: Qual é a diferença entre criar um objeto de threading e chamar invocar sobre isso e apenas chamar BeginInvoke()um delegado? Ou eles são a mesma coisa?

Nathan W
fonte

Respostas:

568

Você quer dizer Delegate.Invoke/ BeginInvokeou Control.Invoke/ BeginInvoke?

  • Delegate.Invoke: Executa de forma síncrona, no mesmo encadeamento.
  • Delegate.BeginInvoke: Executa de forma assíncrona, em um threadpoolencadeamento.
  • 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 Personcom FirstNamee LastNamepropriedades, 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.BeginInvokemaneira "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.

Jon Skeet
fonte
4
Então, por que as pessoas usariam Invoke sobre BeingInvoke? Não deveria haver algumas vantagens sobre o uso de Invoke. Ambos executam processos em segundo plano, apenas um está no mesmo segmento e o outro em um segmento diferente?
yeeen 8/10/10
2
@ Jon: Enquanto estou usando Dispatcher.BeginInvoke meu código está funcionando bem e no Dispatcher.Invoke meu aplicativo me fazendo esperar por alguns segundos, em seguida, inicializa todos os controles e inicia, Você pode me ajudar a descobrir exatamente em que lugar eu estive ?
SharpUrBrain
6
@SharpUrBrain: Control.BeginInvoke é o equivalente a Dispatcher.BeginInvoke, mas para WinForms (enquanto Dispatcher é para WPF e Silverlight).
Jon Skeet
4
@ SharpUrBrain: sugiro que você faça uma pergunta específica em vez de continuar nos comentários - e, claro, verifique se a mesma pergunta já foi feita por outra pessoa primeiro.
Jon Skeet
2
@AZ: Sim, por "no thread da interface do usuário", ele quer dizer no "thread da interface do usuário" específico que possui o identificador desse controle específico. Normalmente, existe apenas um encadeamento da interface do usuário, mas é possível ter vários encadeamentos da interface do usuário e, em aplicativos avançados, existem razões pelas quais você os deseja. Tecnicamente, qualquer encadeamento (normal?) Pode iniciar uma bomba de mensagens da interface do usuário e se tornar um encadeamento da interface do usuário - e, mais tarde, desligar a bomba de mensagens e não ser mais um encadeamento da interface do usuário. (Suponho que isso não seja algo para experimentar um thread do pool de threads).
Rob Parker
46

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.

Tim Stewart
fonte
27

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á executada
  • Invoke() 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).

Sujit
fonte
20

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 ).

KMC
fonte
9

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.

Artigo do MSDN

Aaron Palmer
fonte
8

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

Ingako
fonte