SkillAgentSearch skills...

Threading & Multi-Threading

Padrões de threading em FreePascal/Lazarus — TThread, Synchronize, Queue, TCriticalSection, TEvent, thread-safety, Producer-Consumer e cancelamento

Install / Use

npx skills add delphicleancode/lazarus-spec-kit --skill threading

Installs into whichever agent you are using.

About this skill
♊

Gemini Rules

Gemini CLI config

Quality Score

76/100

Supported Platforms

Gemini CLI

Tags

Our assessment of Threading & Multi-Threading

Threading & Multi-Threading scores 76/100 on our quality scale, 3660th of 4,573 Development & Engineering skills we index.

Its Gemini Rules is 13 KB long, well organised into 21 sections with 12 code examples: a thorough specification that gives an agent plenty to work with.

It has no GitHub stars yet, so there is no community track record; judge it on its content.

Substance
30/30
Structure
20/20
Description
15/15
Adoption
0/20
Freshness
11/15

Maintenance, license and trust

  • The repository was last updated about 6 months ago. That is recent enough to be usable, but agent tooling moves fast, so check the instructions against your agent's current version.
  • No license is declared. By default that means all rights are reserved: you can read it, but reusing or redistributing it is not clearly permitted. Ask the author before building on it commercially.
  • Its trust signals score 74/100, with 3 cautions from licensing, adoption, age or documentation. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

Safety scan

No issues found

Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. An AI review of the same text found nothing harmful.

AI review by kimi-k2.7-code on 2026-10-02. Automated pattern scan on 2026-10-01. It catches known dangerous patterns, not every risk — read a skill before letting an agent act on it.

Threading & Multi-Threading compared with similar skills

All 4 of these similar skills score higher than Threading & Multi-Threading; compare them before choosing.

SkillScoreStarsUpdatedFormat
Threading & Multi-Threading (this skill)by delphicleancode7606mo agoGemini Rules
ai-job-searchby MadsLorentzen10045.3ktodayCLAUDE.md
claude-howtoby luongnv8910041.8k8d agoCLAUDE.md
algorithmic-artby anthropics100177.9k16d agoSKILL.md
interview-meby addyosmani100102.0k5d agoSKILL.md

Frequently asked questions

How do I install Threading & Multi-Threading?
Run npx skills add delphicleancode/lazarus-spec-kit. The install tabs above show the steps for each supported agent.
Which AI agents does Threading & Multi-Threading work with?
It is written for Gemini CLI, as a Gemini Rules file. Other agents that read the same format can often use it too.
Is Threading & Multi-Threading safe to use?
Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. An AI review of the same text found nothing harmful. It declares no license and scores 74/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is Threading & Multi-Threading still maintained?
The repository was last updated about 6 months ago. That is recent enough to be usable, but agent tooling moves fast, so check the instructions against your agent's current version.

name: "Threading & Multi-Threading" description: "Padrões de threading em FreePascal/Lazarus — TThread, Synchronize, Queue, TCriticalSection, TEvent, thread-safety, Producer-Consumer e cancelamento"

Threading & Multi-Threading — Skill

Use esta skill ao trabalhar com threads e tarefas assíncronas em projetos FreePascal/Lazarus.

Quando Usar

  • Ao executar operações demoradas sem bloquear a UI (LCL)
  • Ao criar servidores/workers que processam requisições concorrentes
  • Ao sincronizar acesso a recursos compartilhados
  • Ao implementar cancelamento gracioso de threads

Regra de Ouro do Threading no Lazarus

NUNCA acesse componentes visuais (LCL) diretamente de uma thread secundária. Use TThread.Synchronize ou TThread.Queue para atualizar a UI.

Incompatibilidades FPC vs Delphi

IMPORTANTE: As seguintes primitivas do Delphi NÃO existem no FreePascal:

  • TTask, TTask.Run (PPL)
  • TParallel.For (PPL)
  • IFuture<T>, TFuture<T> (PPL)
  • TInterlocked (classe estática)
  • TMonitor (lock nativo de objeto)
  • TThreadedQueue<T> (fila genérica thread-safe)
  • TThread.CreateAnonymousThread (anonymous methods em threads)
  • TThread.Current.CheckTerminated
  • TThread.NameThreadForDebugging

Use exclusivamente TThread, TCriticalSection, TEvent, TThreadList, TMultiReadExclusiveWriteSynchronizer e funções atômicas nativas (InterlockedIncrement, InterlockedDecrement, InterlockedExchange).

Abordagens Disponíveis no FPC

| Abordagem | Quando Usar | Complexidade | |-----------|-------------|--------------| | TThread (herança) | Controle total, workers permanentes | Média | | TThread.Synchronize | Atualizar UI (bloqueante) | Baixa | | TThread.Queue | Atualizar UI (não-bloqueante, PREFERIR) | Baixa | | TCriticalSection | Seção de exclusão mútua (Enter/Leave) | Baixa | | TThreadList | Lista thread-safe com LockList/UnlockList | Baixa | | TEvent | Sinalização entre threads (WaitFor/SetEvent) | Média | | TMultiReadExclusiveWriteSynchronizer | Cache: muitas leituras, poucas escritas | Média |

TThread — Padrão Principal no FPC

Thread com Herança (Recomendado para Workers)

uses
  Classes, SysUtils;

type
  /// <summary>
  ///   Worker thread para processamento em background.
  ///   Demonstra herança de TThread com cancelamento via Terminated.
  /// </summary>
  TDataProcessorThread = class(TThread)
  private
    FProgressCurrent: Integer;
    FProgressTotal: Integer;
    FErrorMsg: string;
    procedure DoUpdateProgress;
    procedure DoShowError;
  protected
    procedure Execute; override;
  public
    constructor Create;
  end;

constructor TDataProcessorThread.Create;
begin
  inherited Create(True);   // Criar suspensa
  FreeOnTerminate := True;  // Auto-libera ao terminar
end;

procedure TDataProcessorThread.Execute;
var
  I: Integer;
begin
  FProgressTotal := 100;
  try
    for I := 0 to FProgressTotal - 1 do
    begin
      { Verificar cancelamento em cada iteração }
      if Terminated then
        Exit;

      { Processar item }
      ProcessItem(I);
      FProgressCurrent := I + 1;

      { Atualizar UI via Queue (não-bloqueante) }
      Queue(@DoUpdateProgress);
    end;
  except
    on E: Exception do
    begin
      FErrorMsg := E.Message;
      Queue(@DoShowError);
    end;
  end;
end;

procedure TDataProcessorThread.DoUpdateProgress;
begin
  // Acessa LCL de forma segura aqui (roda na main thread)
  frmMain.pbrProgress.Max := FProgressTotal;
  frmMain.pbrProgress.Position := FProgressCurrent;
end;

procedure TDataProcessorThread.DoShowError;
begin
  ShowMessage('Erro: ' + FErrorMsg);
end;

Uso da Thread Dedicada

procedure TfrmMain.btnProcessClick(Sender: TObject);
var
  LThread: TDataProcessorThread;
begin
  LThread := TDataProcessorThread.Create;
  LThread.Start;
end;

procedure TfrmMain.btnCancelClick(Sender: TObject);
begin
  { Solicitar cancelamento gracioso }
  if Assigned(FCurrentThread) then
    FCurrentThread.Terminate;
end;

Synchronize vs Queue

| Método | Comportamento | Quando Usar | |--------|--------------|-------------| | TThread.Synchronize | Bloqueante — espera a main thread processar | Quando precisa do resultado da UI | | TThread.Queue | Não-bloqueante — enfileira e continua | Progresso, logs, atualizações visuais |

{ Synchronize: BLOQUEIA a thread até a main thread processar }
Synchronize(@DoUpdateUI);
// A thread só continua AQUI depois que a main thread executou

{ Queue: NÃO BLOQUEIA — enfileira e continua imediatamente }
Queue(@DoUpdateUI);
// A thread continua IMEDIATAMENTE sem esperar

Recomendação: Prefira Queue sempre que possível. Use Synchronize apenas quando precisar de um resultado da UI de volta na thread.

Thread-Safety — Proteção de Recursos

TCriticalSection (unit syncobjs)

uses
  syncobjs;

type
  TThreadSafeCounter = class
  private
    FCount: Integer;
    FLock: TCriticalSection;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Increment;
    function GetValue: Integer;
  end;

constructor TThreadSafeCounter.Create;
begin
  inherited;
  FLock := TCriticalSection.Create;
  FCount := 0;
end;

destructor TThreadSafeCounter.Destroy;
begin
  FLock.Free;
  inherited;
end;

procedure TThreadSafeCounter.Increment;
begin
  FLock.Enter;
  try
    Inc(FCount);
  finally
    FLock.Leave;  // SEMPRE no finally!
  end;
end;

function TThreadSafeCounter.GetValue: Integer;
begin
  FLock.Enter;
  try
    Result := FCount;
  finally
    FLock.Leave;
  end;
end;

Operações Atômicas Nativas do FPC

{ InterlockedIncrement — operação atômica nativa }
InterlockedIncrement(FProcessedCount);
InterlockedDecrement(FPendingCount);

{ InterlockedExchange — troca atômica }
InterlockedExchange(FOldValue, LNewValue);

{ InterlockedCompareExchange — compare-and-swap }
InterlockedCompareExchange(FTarget, LNewVal, LExpectedVal);

Nota: No FPC são funções livres (InterlockedIncrement), não métodos de classe (TInterlocked.Increment) como no Delphi.

TThreadList (Lista Thread-Safe)

var
  FSharedList: TThreadList;

{ Thread A: adicionar }
LList := FSharedList.LockList;
try
  LList.Add(LItem);
finally
  FSharedList.UnlockList;
end;

{ Thread B: ler }
LList := FSharedList.LockList;
try
  for I := 0 to LList.Count - 1 do
    ProcessItem(LList[I]);
finally
  FSharedList.UnlockList;
end;

TMultiReadExclusiveWriteSynchronizer (MREWS)

type
  TThreadSafeCache = class
  private
    FData: TStringList;
    FLock: TMultiReadExclusiveWriteSynchronizer;
  public
    constructor Create;
    destructor Destroy; override;
    function TryGet(const AKey: string; out AValue: string): Boolean;
    procedure Put(const AKey, AValue: string);
  end;

function TThreadSafeCache.TryGet(const AKey: string; out AValue: string): Boolean;
var
  LIdx: Integer;
begin
  FLock.BeginRead;  // Múltiplas threads podem ler simultaneamente
  try
    LIdx := FData.IndexOfName(AKey);
    Result := LIdx >= 0;
    if Result then
      AValue := FData.ValueFromIndex[LIdx];
  finally
    FLock.EndRead;
  end;
end;

procedure TThreadSafeCache.Put(const AKey, AValue: string);
begin
  FLock.BeginWrite;  // Apenas uma thread pode escrever por vez
  try
    FData.Values[AKey] := AValue;
  finally
    FLock.EndWrite;
  end;
end;

Eventos e Sinalização

TEvent

uses
  syncobjs;

var
  FStopEvent: TEvent;

{ Criar }
FStopEvent := TEvent.Create(nil, True, False, ''); // Manual reset, initially unsignaled

{ Thread: esperar sinal }
procedure TWorkerThread.Execute;
begin
  while not Terminated do
  begin
    { Esperar até 500ms por sinal de parada }
    if FStopEvent.WaitFor(500) = wrSignaled then
      Break;

    { Fazer trabalho }
    DoWork;
  end;
end;

{ Main thread: sinalizar parada }
FStopEvent.SetEvent;

Padrão Producer-Consumer (sem TThreadedQueue)

No FPC não existe TThreadedQueue<T>. Implementamos com TThreadList + TEvent:

type
  TSimpleWorkQueue = class
  private
    FList: TThreadList;
    FNewItemEvent: TEvent;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Push(AItem: Pointer);
    function Pop(ATimeoutMs: Cardinal): Pointer;
  end;

constructor TSimpleWorkQueue.Create;
begin
  inherited;
  FList := TThreadList.Create;
  FNewItemEvent := TEvent.Create(nil, False, False, '');
end;

destructor TSimpleWorkQueue.Destroy;
begin
  FNewItemEvent.Free;
  FList.Free;
  inherited;
end;

procedure TSimpleWorkQueue.Push(AItem: Pointer);
var
  LList: TList;
begin
  LList := FList.LockList;
  try
    LList.Add(AItem);
  finally
    FList.UnlockList;
  end;
  FNewItemEvent.SetEvent; // Sinalizar que há item disponível
end;

function TSimpleWorkQueue.Pop(ATimeoutMs: Cardinal): Pointer;
var
  LList: TList;
begin
  Result := nil;
  if FNewItemEvent.WaitFor(ATimeoutMs) = wrSignaled then
  begin
    LList := FList.LockList;
    try
      if LList.Count > 0 then
      begin
        Result := LList[0];
        LList.Delete(0);
      end;
    finally
      FList.UnlockList;
    end;
  end;
end;

Padrão: Background Worker com Callback

type
  /// <summary>
  ///   Background worker genérico que executa trabalho pesado
  ///   e notifica a main thread via métodos sincronizados.
  /// </summary>
  TBackgroundWorkerThread = class(TThread)
  private
    FResult: string;
    FError: string;
    FOnSuccess: TNotifyEvent;
    FOnError: TNotifyEvent;
    procedure DoSuccess;
    procedure DoError;
  protected
    procedure Execute; override;
    function DoWork: string; virtual; abstract;
  public
    property OnSuccess: TNotifyEvent read FOnSuccess write FOnSuccess;
    property OnError: TNotifyEvent read FOnError write FOnError;
    property ResultValue: string read FResult;
    property ErrorMessage: string read FError;
  end;

procedure TBackgroundWorkerThread.Execute;
begin
  try
    FResult := DoWork;
    if Assigned(FOnSuccess) then
      Queue(@DoSuccess);
  except
    on E: Exception do
    begin
      FError := E.Message;
      if Assigned(FOnError) then
        Queue(@DoError);
    end;
  end;
end;

procedure TBackgroundWorkerThread.DoSuccess;
begin
  if Assigned(FOnSuccess) then
    FOnSuccess(Self);
end;

procedure TBackgroundWorkerThread.DoError;
begin
  if Assigned(FOnError) then
    FOnError(Self);
end;

Padrão: Timer Thread (Execução Periódica)

type
  TTimerThread = class(TThread)
  private
    FInterval: Cardinal;
    procedure DoTimerTick;
  protected
    procedure Execute; override;
    procedure OnTick; virtual; abstract;
  public
    constructor Create(AIntervalMs: Cardinal);
  end;

constructor TTimerThread.Create(AIntervalMs: Cardinal);
begin
  inherited Create(True);
  FreeOnTerminate := False;
  FInterval := AIntervalMs;
end;

procedure TTimerThread.Execute;
begin
  while not Terminated do
  begin
    Sleep(FInterval);
    if not Terminated then
      Synchronize(@DoTimerTick);
  end;
end;

procedure TTimerThread.DoTimerTick;
begin
  OnTick;
end;

Anti-Patterns a Evitar

// ❌ NUNCA acessar UI diretamente de thread secundária
procedure TMyThread.Execute;
begin
  lblStatus.Caption := 'Processando...';  // CRASH ou comportamento imprevisível!
end;

// ✅ SEMPRE usar Synchronize/Queue para UI
procedure TMyThread.Execute;
begin
  Queue(@DoUpdateLabel);  // Seguro!
end;

// ❌ NUNCA criar thread com FreeOnTerminate=True e manter referência
FMyThread := TMyThread.Create(True);
FMyThread.FreeOnTerminate := True;
FMyThread.Start;
FMyThread.WaitFor;  // CRASH! O objeto pode já ter sido liberado!

// ✅ Se precisa de WaitFor, NÃO use FreeOnTerminate
FMyThread := TMyThread.Create(True);
FMyThread.FreeOnTerminate := False;
FMyThread.Start;
FMyThread.WaitFor;
FMyThread.Free;

// ❌ NUNCA acessar variáveis compartilhadas sem proteção
Inc(FSharedCounter);  // Race condition!

// ✅ SEMPRE proteger acesso compart

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars0
CategoryDevelopment
Updated6mo ago
Forks0

Trust signals

74/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

1 medium2 low