Modern applications rely on Multi-Threading to execute heavy or time-consuming operations (like file processing, network requests, or complex calculations) in the background, keeping the user interface responsive.
1. Threads and Concurrency
- Process: An instance of a running application (e.g., your browser). Each process has its own isolated memory space.
- Thread: A single sequence of execution within a process. Every application starts with one Main Thread (the GUI Thread), which handles the UI events (clicks, painting, etc.).
- Multi-Threading: Running two or more threads concurrently.
Problem: If a long operation runs on the Main Thread, the GUI Thread cannot process paint or click messages, causing the application to freeze (“Not Responding”).
Solution: Offload the long task to a Worker Thread.
2. The TThread Class
In Object Pascal (Delphi/Lazarus), the primary way to create a worker thread is by inheriting from the standard class TThread (found in the Classes unit).
A. Creating a Custom Thread
To create a background thread, you must:
- Define a new class inheriting from
TThread. - Override the abstract
Executemethod. This method contains all the code that will run in the background.
unit WorkerThreadUnit;
interface
uses Classes, SysUtils;
type
TMyWorker = class(TThread)
protected
// The core method that runs in the background thread
procedure Execute; override;
public
// Optional data fields for the thread's work
InputData: String;
end;
implementation
{ TMyWorker }
procedure TMyWorker.Execute;
begin
// Check the Terminated flag regularly to allow the thread to stop gracefully
while not Terminated do
begin
// 1. Perform the long-running task here
Writeln('Worker: Starting job for ', InputData);
Sleep(2000); // Simulate 2 seconds of work
// 2. Report progress/results back to the main thread (Crucial step - see below!)
Synchronize(procedure
begin
// Code inside Synchronize runs safely on the Main Thread
Form1.Label1.Caption := 'Job Done for ' + InputData;
end);
Break; // Exit the loop after one job for this example
end;
end;
end.
B. Starting and Stopping the Thread
The thread is started by calling its Start method (which calls Execute in the background) and stopped by calling Terminate (which sets the Terminated flag).
var
Worker: TMyWorker;
begin
Worker := TMyWorker.Create(True); // Create(True) means it is created Suspended
try
Worker.InputData := 'Task A';
Worker.Resume; // Starts the Execute method in the background
// The main program continues immediately without waiting!
Writeln('Main: Waiting for user input...');
// To stop the thread gracefully later:
// Worker.Terminate;
finally
// Wait for the thread to finish and clean up (optional, but safe)
// Worker.WaitFor;
// Worker.Free;
end;
end.
3. Safely Accessing the UI (Synchronize and Queue)
A worker thread MUST NOT directly access or modify any visual component (like TEdit, TButton, TForm) or change any data shared with the main thread. This causes race conditions and memory corruption.
Object Pascal provides two safe mechanisms to execute code back on the Main Thread:
| Method | Purpose | Mechanism |
Synchronize(...) | Executes the anonymous method on the Main Thread and waits for it to finish before the worker thread resumes. | Simple, but can block the worker thread. |
Queue(...) | Executes the anonymous method on the Main Thread and immediately returns to the worker thread without waiting. | More efficient, preferred for fire-and-forget updates. |
The most modern syntax uses Anonymous Methods (P3.2) with Synchronize or Queue inside the worker’s Execute method, as shown in the example above.
4. Parallel Processing Library
For purely computational tasks that can be broken into independent chunks, modern frameworks provide higher-level Parallel Processing Libraries (e.g., the OmniThreadLibrary or the standard TTask in newer Delphi versions).
These libraries abstract away the complexities of manual thread management, allowing you to simply define the work and tell the system how many threads to use.
// Example concept using a TTask-like approach (simplified)
// Task.Run({anonymous method for work}).ContinueWith({anonymous method for UI update});
This functional approach is generally safer and faster for array and data processing than managing TThread objects manually.
