Prioridade do Dispatcher em Aplicações WPF para Gestão de Threads

O DispatcherPriority é uma enumeração essencial no WPF que determina a ordem de execução de operações na fila do Dispatcher, garantindo que tarefas críticas como respostas a input do utilizador tenham precedência sobre operações secundárias.

Quando Utilizar o DispatcherPriority

  • Atualizar elementos da interface a partir de uma thread secundária (ex.: após porcessamento de dados em segundo plano).
  • Controlar a sequência de operações, como executar código após a renderização da UI.
  • Adiar tarefas não urgentes para momentos de inatividade do sistema, evitando bloqueios na interface.
  • Dividir operações longas em fragmentos menores para manter a responsividade da aplicação.

Implementação Prática

Para despachar operações para a UI thread, utiliza-se o método BeginInvoke (assíncrono) ou Invoke (síncrono) do Dispatcher, especificando a prioridade desejada.

using System.Windows.Threading;

// Exemplo: atualizar um controlo a partir de uma tarefa assíncrona
private async Task CarregarDadosAsync()
{
    var resultado = await ObterDadosDoServidorAsync();
    
    // Despacha a atualização da UI com prioridade normal
    Application.Current.Dispatcher.BeginInvoke(
        DispatcherPriority.Normal,
        () => {
            listaItens.ItemsSource = resultado;
        });
}

// Demonstração de diferentes prioridades
private void TestarPrioridades()
{
    var dispatcher = Dispatcher.CurrentDispatcher;
    
    dispatcher.BeginInvoke(DispatcherPriority.Background, () =>
        Console.WriteLine("Tarefa em segundo plano"));
    
    dispatcher.BeginInvoke(DispatcherPriority.Input, () =>
        Console.WriteLine("Resposta a input"));
    
    dispatcher.BeginInvoke(DispatcherPriority.Render, () =>
        Console.WriteLine("Operação durante renderização"));
}

// Verificação de acesso à thread da UI
private void AtualizarEstado(string mensagem)
{
    if (!Dispatcher.CheckAccess())
    {
        Dispatcher.BeginInvoke(DispatcherPriority.Normal,
            () => AtualizarEstado(mensagem));
        return;
    }
    
    textoStatus.Text = mensagem;
}

Níveis de Prioridade Detalhados

Prioridade Valor Descrição
Inativo 0 Operação não é processada
InatividadeDoSistema 1 Executa quando o sistema está ocioso
InatividadeDaAplicação 2 Executa quando a aplicação não está a processar input
ContextoInativo 3 Executa após operações de contexto mais importantes
SegundoPlano 4 Tarefas secundárias como pré-carregamento de dados
Input 5 Relativo a eventos de input do utilizador
Carregado 6 Após eventos de carregamento, antes do processamento de input
Renderização 7 Durante ciclos de renderização da UI
VinculaçãoDeDados 8 Atualizações de data binding
Normal 9 Prioridade padrão para a maioria das atualizações da UI
Envio 10 Máxima prioridade, execução imediata (usar com cautela)

Cuidaods Essenciais

Evitar deadlocks: Prefira BeginInvoke em vez de Invoke sempre que possível, pois este último pode bloquear a thread chamadora se a UI thread estiver ocupada.

// Risco de deadlock se chamado de uma thread não-UI
private void AtualizarUISincronamente()
{
    Dispatcher.Invoke(
        DispatcherPriority.Normal,
        () => { /* operação na UI */ });
}

// Alternativa segura assíncrona
private void AtualizarUIAssincronamente()
{
    Dispatcher.BeginInvoke(
        DispatcherPriority.Normal,
        () => { /* operação na UI */ });
}

Otimização de desempenho: Evite despachar múltiplas operações de alta prioridade em ciclos rápidos; em vez disso, acumule mudanças e atualize a UI em lote.

// Ineficiente: múltiplas chamadas de alta prioridade
for (int i = 0; i < 500; i++)
{
    Dispatcher.BeginInvoke(DispatcherPriority.Render, () =>
        AtualizarElementoVisual());
}

// Melhoria: despacho único com processamento interno
Dispatcher.BeginInvoke(DispatcherPriority.Render, () =>
{
    for (int i = 0; i < 500; i++)
        AtualizarElementoVisual();
});

Cancelamento de operações: Utilize DispatcherOperation para anular operações pendentes, evitando execução desnecessária.

private DispatcherOperation _operacaoPendente;

private void IniciarProcessamento()
{
    _operacaoPendente = Dispatcher.BeginInvoke(
        DispatcherPriority.Background,
        () => ProcessarDadosEmLote());
}

private void CancelarProcessamento()
{
    if (_operacaoPendente?.Status == DispatcherOperationStatus.Pending)
        _operacaoPendente.Abort();
}

Integração com async/await: Combine com InvokeAsync para manter código assíncrono legível.

private async Task ObterEAtualizarAsync()
{
    var dados = await BuscarDadosAsync();
    
    await Application.Current.Dispatcher.InvokeAsync(
        () => {
            gridDados.ItemsSource = dados;
        },
        DispatcherPriority.DataBind);
}

Tags: WPF DispatcherPriority CSharp Multithreading UIThread

Publicado em 6-4 23:43 por Thomas