Threads mean a lot with the latest computer technology. They allow you to perform multiple tasks at the same time without them interfering with each other. For example, imagine a road. If this road is only 1 lane, and there is a large truck driving slowly, then everyone else gets stuck behind that truck and cannot pass. When the road has two lanes, all the faster moving cars can move over to the other lane and pass the slow truck.
Threads allow similar capabilities: One block of code can be running on its own while another block of code is also running on its own - in the same application. By default, any simple application has its own main thread. You have the choice of adding more threads to allow more things to go on at the same time. For example, you might need to write a thread which performs some very lengthy calculations, while allowing the main GUI thread to continue responding.
This example is using the
TThread object which comes with Delphi. There are other ways of performing multi-threaded tasks, including directly using a
Critical Section or by using some other multithreading library, such as OmniThreadLibrary.
Code:
type
TMyThread = class(TThread)
private
FActive: Bool;
FSomeList: TStringList;
procedure SYNC_Something;
procedure SYNC_Started;
procedure SYNC_Stopped;
procedure SetActive(const Value: Bool);
protected
procedure Execute; override;
public
constructor Create;
destructor Destroy; override;
property Active: Bool read FActive write SetActive;
end;
This is a basic structure of a thread object. The property
Active allows you to turn your thread on/off.
Execute is the main procedure where all the threaded work is done. This
must be overridden.
SYNC_Something is a procedure which is called from inside the thread which tells the thread to do something outside the thread. This is simply because you
should not directly call code in one thread from another thread.
Now, for the implementation...
Code:
constructor TMyThread.Create;
begin
inherited Create(True); //You practically always need to pass 'True' because 'False' is just meaningless...
FSomeList:= TStringList.Create;
Resume; //Now that everything is created, we officially start the thread.
end;
destructor TMyThread.Destroy;
begin
FActive:= False; //Make sure our process is stopped...
WaitFor; //Wait for the process to stop before we continue freeing things...
FSomeList.Free;
inherited;
end;
procedure TMyThread.SetActive(const Value: Bool);
begin
//Not necessary to be a procedure setter, but you might want to do some preparation at this point...
FActive:= Value;
end;
procedure TMyThread.Execute;
var
X: Integer;
begin
//Everything done inside here, and any procedures called from inside here, are done from within this thread...
while not Terminated do begin //Keep looping at all times until thread is terminated...
if FActive then begin //Should this thread be doing something?
Synchronize(SYNC_Started);
//START threaded code
FSomeList.Clear;
for X := 1 to 1000 do begin
FSomeList.Append('Some Random Text');
Synchronize(SYNC_Something);
end;
//END threaded code
Synchronize(SYNC_Stopped);
end else begin
Sleep(1); //This keeps the thread from going crazy and maxing the processor...
end;
end;
end;
procedure TMyThread.SYNC_Something;
begin
//Now we can do something which accesses the outside of the thread
//This would presumably trigger an event notifying the main thread that something has been done or needs to be done
end;
procedure TMyThread.SYNC_Started;
begin
//Now we can do something which accesses the outside of the thread
//This would presumably trigger an event notifying the main thread that this thread has begun its work
end;
procedure TMyThread.SYNC_Stopped;
begin
//Now we can do something which accesses the outside of the thread
//This would presumably trigger an event notifying the main thread that this thread has finished its work
end;
Whenever you wish to do something inside the thread which involves contacting the outside of the thread in any way, such as triggering an event, you
need to synchronize these changes. You cannot simply call something like
Form1.Caption := SomeStringInsideTheThread; because there might be something else trying to access this property at the same time, from another thread. Instead, using Synchronize() will allow you to safely contact the outside of the thread.