CS Coder
FireMonkey / Mobile

Tarefas sem travar o FireMonkey: concorrência e duração da tela

Entenda por que mover trabalho para uma tarefa não basta: atualização visual, erros, respostas atrasadas e destruição do formulário precisam de um contrato.

· 5 min de leitura
Objetivo e formato

Planejar uma operação assíncrona sem depender de um formulário que pode desaparecer

Artigo · análise e investigação · Intermediário
Antes de experimentar

Métodos anônimos, exceções e distinção entre thread principal e trabalho em segundo plano

Delphi 11.3 e 13.2 como referência; snippets não compilados neste ambiente

Colocar uma chamada HTTP dentro de uma tarefa pode devolver fluidez à interface. Também pode criar um problema mais difícil: quando a resposta chega, o usuário já fechou a tela ou iniciou outra busca. O trabalho assíncrono precisa de uma regra de duração e de entrega, além de uma thread.

Vamos pensar em uma consulta que recebe um identificador e devolve um texto. O serviço não deve capturar controles visuais. A apresentação decide se o resultado ainda pode ser exibido.

Separe operação e apresentação

Antes de iniciar o trabalho, copie as entradas necessárias na thread da interface. Passe valores para o serviço, em vez de deixar a tarefa ler EditBusca.Text depois.

text
Interface: captura entrada e cria identificador da operação
Serviço: executa trabalho sem acessar controles
Entrega: retorna resultado ou falha
Interface: verifica se ainda aceita essa operação

Se o usuário iniciou uma segunda pesquisa, a primeira resposta pode ter ficado obsoleta. Um identificador crescente ajuda a reconhecer isso, mas ele também precisa de acesso coerente entre threads. Não compartilhe variáveis mutáveis sem um plano de sincronização.

Entenda Queue e Synchronize

TThread.Queue permite encaminhar uma chamada para a thread principal sem bloquear a thread chamadora enquanto aguarda essa execução. Quando chamado pela própria thread principal, pode executar diretamente; ForceQueue é a alternativa para exigir enfileiramento. Documentação de Queue.

Synchronize executa a chamada na thread principal e suspende a thread chamadora durante esse processamento. Essa diferença importa quando há esperas e dependências entre threads. Documentação de Synchronize.

Encaminhar uma chamada à thread principal não garante que o objeto capturado ainda exista. Uma closure que menciona o formulário não o torna automaticamente uma referência segura até a entrega.

Um snippet com uma finalidade limitada

O trecho abaixo mostra apenas o encaminhamento de uma mensagem calculada em uma thread de trabalho. Use System.Classes e chame esse código dentro da operação em segundo plano:

delphi
TThread.Queue(nil,
  procedure
  begin
    // Entregar a mensagem a um consumidor com duração garantida.
    // Não capturar um formulário que pode ter sido destruído.
  end);

O comentário é intencional: preencher esse bloco com LabelResultado.Text := ... sem definir a duração da tela esconderia o principal risco. Esse trecho não é um controlador assíncrono pronto.

Defina o contrato de encerramento

Há várias estratégias possíveis. Uma operação pode ser administrada por um controlador que vive mais que a tela; a tela pode se desinscrever antes de ser destruída; ou a navegação pode aguardar um encerramento coordenado. Escolha uma estratégia explícita e teste a corrida entre entrega e fechamento.

Não confunda solicitar cancelamento com interromper imediatamente uma chamada bloqueante. O serviço precisa observar o sinal em pontos apropriados e ter limites de tempo. O timeout de rede faz parte desse desenho.

Evite esperar pela tarefa na thread da interface se a própria tarefa precisa dessa thread para concluir. Essa dependência pode produzir bloqueio mútuo. Também não use Application.ProcessMessages como solução geral: ele pode permitir reentrada em eventos durante uma operação incompleta.

Erros e estados precisam voltar

Uma falha deve encerrar o estado de carregamento da mesma maneira que um sucesso, desde que a operação ainda seja aceita. Diferencie resultado vazio de falha. Apresente uma mensagem útil e registre o diagnóstico sem tokens ou dados pessoais.

Desabilitar um botão durante a consulta é apenas uma escolha de apresentação. A operação ainda deve lidar com saída da tela, interrupção de rede e resposta atrasada.

Um roteiro que encontra as corridas

Teste busca seguida de outra busca, fechamento imediato, timeout, retorno do segundo plano e erro antes da entrega. Repita com demora artificial no serviço. O resultado esperado é que uma resposta antiga não substitua a atual e que não haja acesso a objetos já destruídos.

Não houve execução desses testes em Delphi aqui. Para organizar o tratamento da falha, leia Exceções e recursos.

Operação, entrega e encerramento

  1. Capture valores e geração; referência ao formulário não garante vida.
  2. Na entrega, contexto válido confere geração e descarta resposta antiga.
  3. Atualize UI na thread principal; erro e cancelamento têm estados.
  4. Ao fechar, invalide geração e remova destino antes da destruição.
  5. Termine operação depois do fechamento: callback não pode acessar controles destruídos.

TThread.Queue agenda, não prolonga vida. Roteiro/snippets não constituem exemplo integrado testado; build FMX e ensaio de corridas dependem do ambiente visual.

Projeto de estudo disponível

Baixar fonte FMX com .pas e .fmx. Inclui layout rolável, registro de geometria e tarefa local com contexto de entrega e geração. O worker captura valores; o contexto perde o destino antes da destruição e respostas de gerações antigas são descartadas.

Compilado para Win32 com DCC32 37.0 em 08/10/2026. Relatório. Não executado visualmente: testes de dispositivo, geometria/rotação e corridas de encerramento permanecem pendentes. A referência anterior a projeto pendente se refere à integração validada no alvo; este pacote entrega fonte de estudo, sem alegar esse teste.