Skip to content
Development
Skill

/threading

Threading patterns in Delphi — TThread, TTask, TParallel, Synchronize, Queue, thread-safety, Producer-Consumer, pools, cancellation and debugging

From plugin
delphi-spec-kit
5218 skills1 command
Install
$ npx -y skills add delphicleancode/delphi-spec-kit --skill threading --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/threading

Context preview

The summary Claude sees to decide when to auto-load this skill.

Threading patterns in Delphi — TThread, TTask, TParallel, Synchronize, Queue, thread-safety, Producer-Consumer, pools, cancellation and debugging

SKILL.md

threading.SKILL.md
name: "Threading & Multi-Threading"
description: "Threading patterns in Delphi — TThread, TTask, TParallel, Synchronize, Queue, thread-safety, Producer-Consumer, pools, cancellation and debugging"

Threading & Multi-Threading — Skill

Use this skill when working with threads, asynchronous tasks and parallelism in Delphi projects.

When to Use

  • When performing time-consuming operations without blocking the UI (VCL/FMX)
  • When implementing parallel data processing
  • When creating servers/workers that process concurrent requests
  • When synchronizing access to shared resources
  • When managing thread pools and work queues
  • By implementing graceful thread cancellation

Golden Rule of Threading in Delphi

> **NEVER access visual components (VCL/FMX) directly from a secondary thread.** > Use `TThread.Synchronize` or `TThread.Queue` to update the UI.

Available Approaches

| Approach | When to Use | Complexity | |-----------|-------------|-------------| | `TThread` | Full control, long-running threads | Average | | `TThread.CreateAnonymousThread` | Simple, one-shot tasks | Low | | `TTask` (PPL) | Modern parallelism, lightweight tasks | Low | | `TParallel.For` (PPL) | Parallel loops in collections | Low | | `TFuture<T>` (PPL) | Asynchronous result with return value | Low | | `TThreadPool` | Reusable Thread Pool | Average | | Dedicated thread (inheritance) | Permanent workers, servers, queues | High |

TThread — Classical Approach

Thread with Inheritance (Recommended for Workers)

type
  ///<summary>
  ///Worker thread for background processing.
  ///Demonstrates TThread inheritance with cancellation via Terminated.
  ///</summary>
  TDataProcessorThread = class(TThread)
  private
    FItems: TThreadList<string>;
    FOnProgress: TProc<Integer, Integer>;
    FOnComplete: TProc<Boolean>;
  protected
    procedure Execute; override;
  public
    constructor Create(AItems: TThreadList<string>);
    property OnProgress: TProc<Integer, Integer> write FOnProgress;
    property OnComplete: TProc<Boolean> write FOnComplete;
  end;

constructor TDataProcessorThread.Create(AItems: TThreadList<string>);
begin
  inherited Create(True);   //Create dropdown
  FreeOnTerminate := True;  //Auto-releases when finished
  FItems := AItems;
end;

procedure TDataProcessorThread.Execute;
var
  LList: TList<string>;
  LTotal, I: Integer;
begin
  try
    LList := FItems.LockList;
    try
      LTotal := LList.Count;
    finally
      FItems.UnlockList;
    end;

    for I := 0 to LTotal - 1 do
    begin
      { Verificar cancelamento em cada iteração }
      if Terminated then
        Exit;

      { Processar item }
      ProcessItem(I);

      { Atualizar UI via Queue (não-bloqueante) }
      if Assigned(FOnProgress) then
        TThread.Queue(nil,
          procedure
          begin
            FOnProgress(I + 1, LTotal);
          end);
    end;

    { Notificar conclusão na main thread }
    if Assigned(FOnComplete) then
      TThread.Queue(nil,
        procedure
        begin
          FOnComplete(not Terminated);
        end);
  except
    on E: Exception do
    begin
      TThread.Queue(nil,
        procedure
        begin
          raise EThreadException.Create('Erro no processamento: ' + E.Message);
        end);
    end;
  end;
end;

Use of Dedicated Thread

procedure TfrmMain.btnProcessClick(Sender: TObject);
var
  LThread: TDataProcessorThread;
begin
  LThread := TDataProcessorThread.Create(FSharedItems);
  LThread.OnProgress :=
    procedure(ACurrent, ATotal: Integer)
    begin
      pbrProgress.Max := ATotal;
      pbrProgress.Position := ACurrent;
      lblStatus.Caption := Format('Processando %d de %d...', [ACurrent, ATotal]);
    end;
  LThread.OnComplete :=
    procedure(ASuccess: Boolean)
    begin
      if ASuccess then
        ShowMessage('Concluído com sucesso!')
      else
        ShowMessage('Processamento cancelado.');
    end;
  LThread.Start;  //Iniciar a thread
end;

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

CreateAnonymousThread (Simple Tasks)

///<summary>
///Simplest way to run code in the background.
///Ideal for one-shot tasks without the need for advanced control.
///</summary>
procedure TfrmMain.LoadDataAsync;
begin
  btnLoad.Enabled := False;

  TThread.CreateAnonymousThread(
    procedure
    var
      LData: TStringList;
    begin
      LData := TStringList.Create;
      try
        { Trabalho pesado (thread secundária — OK!) }
        LData.LoadFromFile('C:\dados\arquivo_grande.csv');
        Sleep(2000); //Simulate processing

        { Atualizar UI (DEVE usar Synchronize ou Queue) }
        TThread.Synchronize(nil,
          procedure
          begin
            mmoOutput.Lines.Assign(LData);
            btnLoad.Enabled := True;
            lblStatus.Caption := Format('Carregados %d registros', [LData.Count]);
          end);
      finally
        LData.Free;
      end;
    end
  ).Start;
end;

Synchronize vs Queue

| Method | Behavior | When to Use | |--------|--------------|-------------| | `TThread.Synchronize` | **Blocking** — waits for the main thread to process | When you need the UI result | | `TThread.Queue` | **Non-blocking** — queue and continue | Progress, logs, visual updates |

{ Synchronize: BLOQUEIA a thread até a main thread processar }
TThread.Synchronize(nil,
  procedure
  begin
    lblStatus.Caption := 'Processando...';
  end);
//The thread only continues HERE after the main thread has executed the code above

{ Queue: NÃO BLOQUEIA — enfileira e continua imediatamente }
TThread.Queue(nil,
  procedure
  begin
    lblStatus.Caption := 'Processando...';
  end);
//The thread continues IMMEDIATELY, without waiting for the main thread

> **Recommendation:** Prefer `Queue` whenever possible. Use `Synchronize` only when you need a result from the UI back

Read more
Ships withdelphi-spec-kit

An opinionated ecosystem of rules, skills and steerings to elevate Delphi development to state-of-the-art with Artificial Intelligence.

Get the whole plugin
Stats
52
Stars
14
Forks
Maintained
Maintenance
MIT
License
5mo ago
Last commit
6mo ago
Created

Repo: delphicleancode/delphi-spec-kit

Other skills on delphi-spec-kit.