Sincronização de Threads com a Interface Gráfica em Delphi

Sincronização de Threads com a Interface Gráfica em Delphi
Sincronizar Threads e Interface Gráfica

Introdução

Nesse post iremos ver como funciona a Sincronização de Threads com a Interface Gráfica em Delphi.

A programação com threads é uma prática comum para executar tarefas em segundo plano sem bloquear a interface do usuário. No entanto, atualizar a interface gráfica a partir de uma thread pode ser um desafio. Este artigo aborda como sincronizar threads com a interface gráfica em Delphi, utilizando TThread.Synchronize e TThread.Queue.

O que são Threads?

Threads são fluxos de execução independentes que podem executar tarefas simultaneamente dentro de uma aplicação. Em Delphi, a classe TThread é usada para criar e gerenciar threads. Threads são úteis para tarefas que demoram a ser concluídas, como operações de E/S, cálculos intensivos e acesso a banco de dados.

Problema com a Atualização da Interface Gráfica

A interface gráfica de uma aplicação Delphi (VCL) não é thread-safe, o que significa que acessar ou modificar componentes visuais diretamente de uma thread pode causar comportamentos inesperados ou falhas. Para evitar isso, devemos sincronizar o acesso à interface gráfica.

TThread.Synchronize

TThread.Synchronize é um método que permite executar um código no contexto do thread principal, garantindo que o acesso à interface gráfica seja seguro. O código passado para Synchronize é executado de forma síncrona, ou seja, a thread secundária espera até que o código termine de executar no thread principal.

type
  TMyThread = class(TThread)
  protected
    procedure Execute; override;
    procedure UpdateUI;
  end;

procedure TMyThread.Execute;
begin
  // Código que a thread executa em segundo plano
  // ...
  Synchronize(UpdateUI);
end;

procedure TMyThread.UpdateUI;
begin
  // Código para atualizar a interface gráfica
  Form1.Label1.Caption := 'Thread terminou a execução';
end;

No exemplo acima, UpdateUI é um método que atualiza um componente de interface gráfica. Synchronize garante que UpdateUI seja executado no contexto do thread principal.

TThread.Queue

TThread.Queue é semelhante a Synchronize, mas a execução do código passado é enfileirada e ocorre de forma assíncrona. Isso significa que a thread secundária não precisa esperar o término da execução no thread principal.

type
  TMyThread = class(TThread)
  protected
    procedure Execute; override;
    procedure QueueUIUpdate;
  end;

procedure TMyThread.Execute;
begin
  // Código que a thread executa em segundo plano
  // ...
  Queue(QueueUIUpdate);
end;

procedure TMyThread.QueueUIUpdate;
begin
  // Código para atualizar a interface gráfica
  Form1.Label1.Caption := 'Thread terminou a execução (assíncrono)';
end;

Quando usar Synchronize e Queue?

  • Synchronize: Use quando a thread secundária precisa esperar pela conclusão da atualização da interface gráfica antes de continuar a execução. Isso é útil quando a atualização da UI está diretamente relacionada à lógica de negócio da thread.
  • Queue: Use quando a atualização da interface gráfica não precisa ser concluída imediatamente. Isso permite que a thread secundária continue sua execução sem esperar, melhorando a performance.

Exemplo Prático: Contador

Vamos criar um exemplo prático onde uma aplicação Delphi atualiza um contador em uma etiqueta na interface gráfica.

  1. Criar o Projeto
    • Crie um novo projeto VCL Forms Application no Delphi.
  2. Adicionar Componentes
    • Adicione um TButton e um TLabel no formulário.
  3. Criar a Thread do Contador
    • Crie uma nova unit para a thread do contador.
unit untContador;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TCounterThread = class(TThread)
  private
    FCount: Integer;
    FLabel: TLabel;
    procedure UpdateLabel;
  protected
    procedure Execute; override;
  public
    constructor Create(ALabel: TLabel);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TCounterThread }

constructor TCounterThread.Create(ALabel: TLabel);
begin
  inherited Create(False);
  FLabel := ALabel;
  FreeOnTerminate := True;
end;

procedure TCounterThread.Execute;
begin
  FCount := 0;
  while not Terminated and (FCount < 100) do
  begin
    Inc(FCount);
    Sleep(100); // Simula uma operação de longa duração
    Synchronize(UpdateLabel); // Atualiza a interface gráfica de forma segura Utilizando Syncronize
    //Você poderia substituir a linha acima pela linha abaixo caso gostaria que a execução fosse enfileirada
    //Queue(UpdateLabel); // Atualiza a interface gráfica de forma segura Utilizando Queue
  end;
end;

procedure TCounterThread.UpdateLabel;
begin
  FLabel.Caption := 'Contagem: ' + IntToStr(FCount);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  TCounterThread.Create(Label1);
end;

end.
  1. Iniciar a Thread no Formulário Principal
    • Adicione o código para iniciar a thread no evento OnClick do botão.
procedure TForm1.Button1Click(Sender: TObject);
begin
  TDownloadThread.Create(ProgressBar1, Label1).Start;
end;

Explicação do Código

  1. Declaração da Classe TCounterThread:
    • TCounterThread herda de TThread e é responsável por executar a contagem em segundo plano.
    • A classe possui um campo FCount para armazenar o contador e um campo FLabel para referenciar o TLabel que será atualizado.
    • O construtor Create inicializa o FLabel e define FreeOnTerminate como True para liberar a memória automaticamente quando a thread terminar.
  2. Método Execute:
    • O método Execute é sobrescrito para implementar a lógica da thread.
    • O loop while incrementa FCount e chama Sleep(100) para simular uma operação de longa duração.
    • Synchronize(UpdateLabel) é usado para chamar UpdateLabel de forma segura no contexto da thread principal.
  3. Método UpdateLabel:
    • UpdateLabel atualiza o texto do TLabel com o valor atual de FCount.
  4. Evento Button1Click:
    • Quando o botão é clicado, uma nova instância de TCounterThread é criada, passando o TLabel para a thread.

Este exemplo demonstra como sincronizar uma thread com a interface gráfica usando TThread.Synchronize, garantindo que as atualizações da interface sejam feitas de forma segura e mantendo a aplicação responsiva.

Veja abaixo a ilustração do projeto utilizando Sincronização de Threads com Interface Gráfica:

Sincronização de Threads com a Interface Gráfica em Delphi
Ilustração do projeto.

Código fonte do exemplo

Você pode fazer o download do exemplo do projeto através do repositório do github:

https://github.com/Gisele-de-Melo/Sincronizar_Thread_Com_Interface_Grafica

Conclusão

Sincronizar threads com a interface gráfica é essencial para garantir a segurança e a estabilidade das aplicações Delphi. O uso correto de TThread.Synchronize e TThread.Queue permite que desenvolvedores atualizem a UI de forma eficiente e segura. Com este conhecimento, você pode criar aplicações Delphi que executam tarefas em segundo plano sem comprometer a responsividade da interface do usuário.

Posts Relacionados


Livros + e-books

Delphi para Android e iOS

codedelphi.com
codedelphi.com

Delphi Start

codedelphi.com

Programando em Delphi XE

Aprenda programar Delphi

Banco de Dados RAD Delphi

Delphi Programming Projects

Deixe um comentário