Modern applications must remain responsive while performing long-running or computationally intensive tasks (like complex calculations, large file processing, or network calls). Threading is the solution, allowing concurrent execution.
1. Threads and Responsiveness
- Main Thread (VCL/FMX): Every Delphi GUI application starts with a single Main Thread. This thread is responsible for handling all user interface (UI) interactions, painting controls, and processing events. If a long operation blocks the main thread, the UI freezes (“freezing the glass”).
- Worker Thread: A separate thread created to execute background tasks, leaving the main thread free to handle the UI.
Thread Rule: Only the Main Thread can directly access or modify visual components (e.g., setting
TLabel.Captionor callingTForm.Show). Attempting to update the UI from a worker thread will lead to unpredictable crashes or visual corruption.
2. The TThread Class
The classic way to manage worker threads in Object Pascal is to create a descendant of the TThread class (found in the System.Classes unit).
A. Creating a Custom Thread
You create a custom thread class and override its mandatory Execute method. The Execute method contains the code that runs concurrently.
// In the Interface Section
type
TMyWorkerThread = class(TThread)
protected
// This is the thread's entry point—the code that runs concurrently
procedure Execute; override;
public
// Optional: Add a property to hold the result
ResultValue: Integer;
end;
// In the Implementation Section
procedure TMyWorkerThread.Execute;
var
i: Integer;
begin
// Set FreeOnTerminate to true so the OS frees memory automatically when done
FreeOnTerminate := True;
// Perform the long-running task
ResultValue := 0;
for i := 1 to 100000000 do
Inc(ResultValue);
end;
B. Controlling the Thread
procedure TForm1.btnStartThreadClick(Sender: TObject);
var
Worker: TMyWorkerThread;
begin
// 1. Create the thread object
Worker := TMyWorkerThread.Create(True); // Create(True) means it is created suspended
// 2. Set properties (optional)
// Worker.Priority := tpLower;
// 3. Start execution
Worker.Start; // Releases the thread to run
end;
3. Synchronizing Access to the UI
To update a visual component from a worker thread, you must use a synchronization method to safely transfer the operation back to the main thread.
The Synchronize method is the standard way to do this. It pauses the worker thread and forces the execution of a specific anonymous method (or procedure) in the main thread’s context.
// Inside the TMyWorkerThread.Execute method:
procedure TMyWorkerThread.Execute;
begin
// ... long task calculation ...
ResultValue := 100;
// Safely update the UI component (lblStatus is a component on TForm1)
Synchronize(
procedure // This anonymous method runs on the Main Thread
begin
// Direct UI access is now safe
TForm1(Form1).lblStatus.Caption := 'Calculation Complete. Result: ' + IntToStr(ResultValue);
end
);
end;
4. Parallel Programming Library (PPL)
For modern multi-core performance, Delphi provides the Parallel Programming Library (PPL) in the System.Threading unit. PPL simplifies multi-threading for common tasks, such as parallel loops.
- PPL Unit: Requires
System.Threadingin theusesclause. TTask: The PPL component for running an independent operation asynchronously.TParallel.For: Used to automatically split aforloop iteration across multiple available CPU cores.
Example: Using TTask
// Requires System.Threading in uses
procedure TForm1.btnStartTaskClick(Sender: TObject);
begin
// Create a TTask object and pass it an anonymous method to run in the background
TTask.Create(
procedure
var
Total: Integer;
begin
// Long-running code executes here in a worker thread
Total := CalculateLargeData();
// Use TThread.Queue for a safe, asynchronous UI update
TThread.Queue(nil, // nil means queue to the main thread
procedure
begin
lblResult.Caption := 'PPL Task Finished: ' + IntToStr(Total);
end
);
end
).Start; // Start the background task
end;
TThread.Queue: Similar toSynchronize, but asynchronous. It queues the UI update and returns immediately, allowing the worker thread to continue working (or terminate) without waiting for the UI update to execute.
