Problema
Temos diversas rotinas desenvolvidas em Delphi executando integrações variadas, onde aquelas no qual realiza geração de arquivos com base em layout com informações como por exemplo vendas, NF, produtos, clientes, estoque, etc; eventualmente apresenta alguns problemas de lentidão e travamentos nesses processos devido o grande volume de dados da base de dados necessários para tal tarefas.
Solução
Neste post, vou mostrar um exemplo bastante básico de como criar um processamento usando Thread em Delphi, isso resolve o problema pois passa a execução da consulta e montagem de arquivo para uma Thread separada da Thread principal do Form da tela.
1 - Primeiro crie uma nova classe para a Thread no Delphi, menu File -> New -> Other -> Thread Object para criar a Thread mas isso depende da versão do Delphi. O esqueleto gerado será basicamente o reproduzido abaixo:
TWProcessamentoThread = class(TThread) protected procedure Execute; override; end;
2 - O código gerado não sobrescreve o construtor padrão. Como teremos uma interação com o Form principal da aplicação, será preciso montar um construtor especial que aceite o nosso Form como parâmetro:
var _Form: TForm; constructor TWProcessamentoThread.Create(CreateSuspended: Boolean; AForm: TForm); begin inherited Create (CreateSuspended); _Form := AForm; FreeOnTerminate := false; end;
A primeira linha do construtor chama o construtor Create da classe base (inherited) passando-lhe o parâmetro para indicar se a thread deve iniciar imediatamente ou permanecer suspensa. FreeOnTerminate é ajustado como false de modo que nós teremos que destruir instâncias da classe manualmente.
3 - A procedure Execute é onde deve ficar o código executado pela Thread. Aqui onde e criado a variável uFormPrincipal esta acessando o Form principal para obter os filtros inseridos pelo cliente, e nessa procedure será implementado o comportamento da Thread.
procedure TWProcessamentoThread.Execute; var uFormPrincipal : TfrmPrincipal; begin uFormPrincipal := _Form As TfrmPrincipal; // Implementação das consultas e geração de arquivos end;
4 - No seu Form principal ao clique do botão Iniciar o processo crie uma instância da Thread para execução imediata e implemente um TTimer no Form principal com intervalo de um segundo ligando o Timer que checa o status da execução dela assim que instanciar a Thread:
var _Thread: ThreadProcesso; _Thread := TWProcessamentoThread.Create (false, Self); Timer1.Enabled := true;
Na classe da Thread crie uma variável concluiuProcessamento do tipo boolean e passe ela para true no final do processamento.
5 - No evento do Timer do Form principal, verificamos se a Thread chegou ao fim da execução para fazer a limpeza:
if (_Thread.concluiuProcessamento) then begin Timer1.Enabled := false; _Thread.Free; _Thread := Nil; end;
6 - Muito importante matar a Thread se o cliente fechar a rotina antes de concluir, para isso no Form principal no evento de saída FormDestroy implemente o trecho abaixo.
if TimerThread.Enabled then _Thread.Terminate;
7 - Para mais detalhes, verifique o projeto da rotina 2621 pois ela tem a implementação que fiz de Thread no Delphi que mostro nesse exemplo.