Invoke e BeginInvoke no C#: Diferenças e Uso Prático

Em aplicações Windows Forms com C#, a atualização segura de controles de interface do usuário a partir de threads secundários requer o uso dos métodos Invoke e BeginInvoke. Estes métodos pertencem à classe Control e permitem a execução de delegados de forma controlada na thread principal.

Distinção Fundamental antre Invoke e BeginInvoke

O método Control.Invoke executa um delegado de maneira síncrona no thread que detém o handle da janela do controle. A thread que aciona o Invoke fica bloqueada até a conclusão da execução do delegado.

Já o Control.BeginInvoke agenda um delegado para execução assíncrona no thread que criou o handle do controle. A thread chamadora prossegue imediatamente, sem aguardar a finalização do delegado.

Portanto, Invoke proporciona sincronia em relação à thread de trabalho, enquanto BeginInvoke oferece assincronia, mantendo a execução do código na thread de IU sempre segura.

Quando Utilizar Cada Método

Opte por Invoke quendo a thread de trabalho necessitar atualizar a interface e precisar esperar que essa atualização seja concluída para continuar o fluxo.

Empregue BeginInvoke se a thread de trabalho puder avançar imediatamente após o agendamento da atualização da interface, sem necessidade de sincronização imediata.

Exemplo com Execução Síncrona (Invoke)

O código abaixo demonstra o uso de Invoke, onde a thread de trabalho aguarda a conclusão da atualização na interface:

private void AoClicarBotao(object sender, EventArgs args) { MessageBox.Show("ID da thread principal: " + Thread.CurrentThread.ManagedThreadId);

Thread worker = new Thread(ProcessarDados);
worker.Start();

for (int cont = 0; cont < 3; cont++)
{
    Thread.Sleep(400);
}

}

private void ProcessarDados() { MessageBox.Show("ID da thread de trabalho: " + Thread.CurrentThread.ManagedThreadId);

Invoke(new Action(() =>
{
    meuBotao.Text = "Carregado";
    MessageBox.Show("Delegado executado na thread: " + Thread.CurrentThread.ManagedThreadId);
}));

MessageBox.Show("Thread de trabalho finalizou: " + Thread.CurrentThread.ManagedThreadId);

}


</div>Neste caso, a thread de trabalho é suspensa até que a ação dentro de Invoke seja concluída na thread principal.

Exemplo com Execução Assíncrona (BeginInvoke)
---------------------------------------------

Utilizando BeginInvoke para operação não bloqueante:

<div>```

private void AoClicarBotao(object sender, EventArgs args)
{
    MessageBox.Show("ID da thread principal: " + Thread.CurrentThread.ManagedThreadId);

    Thread worker = new Thread(ProcessarDados);
    worker.Start();

    for (int cont = 0; cont < 3; cont++)
    {
        Thread.Sleep(400);
    }
}

private void ProcessarDados()
{
    MessageBox.Show("ID da thread de trabalho: " + Thread.CurrentThread.ManagedThreadId);

    BeginInvoke(new Action(() =>
    {
        meuBotao.Text = "Carregado";
        MessageBox.Show("Delegado executado na thread: " + Thread.CurrentThread.ManagedThreadId);
    }));

    MessageBox.Show("Thread de trabalho continuou: " + Thread.CurrentThread.ManagedThreadId);
}

Solução para Acesso Cruzado de Threads

Em programação multithread, acessar diretamente propriedades de controles de IU de threads exetrnas resulta em exceções. A utilização de Invoke ou BeginInvoke garante que o código de atualização seja executado na thread correta.

Uma abordagem comum encapsula a lógica de atualização em um método que verifica a necessidade de invocação:

private void DefinirTextoControle(string texto) { if (caixaTexto.InvokeRequired) { Invoke(new Action(() => caixaTexto.Text = texto)); } else { caixaTexto.Text = texto; } }


</div>Técnicas Adicionais e Simplificações
------------------------------------

Embora seja possível desativar as verificações de thread com a propriedade `Control.CheckForIllegalCrossThreadCalls`, isso não é recomendado por comprometer a segurança de concorrência.

Expressões lambda em versões recentes do C# permitem uma sintaxe mais concisa para delegados:

<div>```

private void AtualizarInterface()
{
    Invoke(() =>
    {
        meuLabel.Text = "Valor atualizado";
    });
}

Tags: C# Windows Forms Multithreading Invoke BeginInvoke

Publicado em 6-18 20:25