Thread in Free Pascal and Delphi is a process that run simutanously with main program. This is very useful when we need to process data in background while user doing his own work. And is needed to maximize processing in multicore processor because a process including main process can only use 1 core and a quad core processor require a minimum of 4 threads to use all 4 cores.
Basically to create a thread we need to create the thread class and override the execute procedure with the thread code. To minimize work I create WorkerThreadList class, a very simple class to run a procedure of object as a thread.
First the class definition:
- type
- { TSemaphore }
- TSemaphore = class(TObject)
- private
- FSem: Integer;
- public
- constructor Create;
- destructor Destroy; override;
- procedure Lock;
- procedure Unlock;
- end;
- { WorkerThreads Routines }
- TWorkerThread = class;
- TWorkerThreadList = class;
- TWorkEvent = procedure(WT: TWorkerThread) of object;
- { TWorkerThread }
- TWorkerThread = class(TThread)
- private
- FOwner: TWorkerThreadList;
- FOnWork: TWorkEvent;
- FParam: Pointer;
- protected
- procedure Execute; override;
- public
- constructor Create(AOwner: TWorkerThreadList; AOnWork: TWorkEvent;
- AParam: Pointer);
- destructor Destroy; override;
- property OnWork: TWorkEvent read FOnWork write FOnWork;
- property Param: Pointer read FParam;
- property Terminated;
- end;
- { TWorkerThreadList }
- TWorkerThreadList = class(TObject)
- private
- FWorkers: TObjectList;
- FSem: TSemaphore;
- function GetItems(Index: Integer): TWorkerThread;
- public
- constructor Create;
- destructor Destroy; override;
- function AddNew(AOnWork: TWorkEvent; AParam: Pointer=nil): TWorkerThread;
- procedure TerminateWait(Thread: TWorkerThread);
- procedure UnregisterWorkerThread(AThread: TWorkerThread);
- procedure TerminateWorkerThreads(Wait: Boolean);
- property Items[Index: Integer]: TWorkerThread read GetItems; default;
- end;
Then I create the class definition like this:
- { TWorkerThreadList }
- function TWorkerThreadList.GetItems(Index: Integer): TWorkerThread;
- begin
- Result := TWorkerThread(FWorkers[Index]);
- end;
- constructor TWorkerThreadList.Create;
- begin
- inherited;
- FWorkers := TObjectList.Create(False);
- FSem := TSemaphore.Create;
- end;
- destructor TWorkerThreadList.Destroy;
- begin
- TerminateWorkerThreads(True);
- FWorkers.Free;
- FSem.Free;
- inherited Destroy;
- end;
- function TWorkerThreadList.AddNew(AOnWork: TWorkEvent;
- AParam: Pointer): TWorkerThread;
- var
- N: TWorkerThread;
- begin
- N := TWorkerThread.Create(Self, AOnWork, AParam);
- try
- FSem.Lock;
- try
- FWorkers.Add(N);
- finally
- FSem.Unlock;
- end;
- Result := N;
- N.Start;
- except
- N.Free;
- raise;
- end;
- end;
- procedure TWorkerThreadList.TerminateWait(Thread: TWorkerThread);
- begin
- if (Thread = nil) then exit;
- FSem.Lock;
- try
- if (FWorkers.IndexOf(Thread) < 0) then
- exit
- else begin
- if (not Thread.Terminated) then Thread.Terminate;
- if (Thread.Suspended) then Thread.Start;
- end;
- finally
- FSem.Unlock;
- end;
- repeat
- FSem.Lock;
- try
- if (FWorkers.IndexOf(Thread) < 0) then exit;
- finally
- FSem.Unlock;
- end;
- Sleep(1);
- until (False);
- end;
- procedure TWorkerThreadList.UnregisterWorkerThread(AThread: TWorkerThread);
- begin
- if ((AThread = nil) or (AThread.FOwner <> Self)) then
- exit;
- FSem.Lock;
- try
- FWorkers.Remove(AThread);
- AThread.FOwner := nil;
- finally
- FSem.Unlock;
- end;
- end;
- procedure TWorkerThreadList.TerminateWorkerThreads(Wait: Boolean);
- var
- I, J: Integer;
- begin
- repeat
- FSem.Lock;
- try
- J := FWorkers.Count;
- for I := 0 to J-1 do
- begin
- with TWorkerThread(FWorkers[I]) do
- begin
- if (not Terminated) then Terminate;
- if (Suspended) then Start;
- end;
- end;
- finally
- FSem.Unlock;
- end;
- Sleep(1);
- until ((J = 0) or not Wait);
- end;
- { TWorkerThread }
- procedure TWorkerThread.Execute;
- begin
- try
- if (Assigned(FOnWork)) then FOnWork(Self);
- except
- end;
- if (FOwner <> nil) then FOwner.UnregisterWorkerThread(Self);
- end;
- constructor TWorkerThread.Create(AOwner: TWorkerThreadList; AOnWork: TWorkEvent;
- AParam: Pointer);
- begin
- inherited Create(True);
- FreeOnTerminate := True;
- Priority := tpIdle;
- FOwner := AOwner;
- FOnWork := AOnWork;
- FParam := AParam;
- end;
- destructor TWorkerThread.Destroy;
- begin
- if (FOwner <> nil) then FOwner.UnregisterWorkerThread(Self);
- inherited Destroy;
- end;
- { TSemaphore }
- constructor TSemaphore.Create;
- begin
- FSem := 0;
- end;
- destructor TSemaphore.Destroy;
- begin
- Unlock;
- inherited Destroy;
- end;
- procedure TSemaphore.Lock;
- begin
- while (InterLockedExchange(FSem, 1) = 1) do Sleep(1);
- end;
- procedure TSemaphore.Unlock;
- begin
- InterLockedExchange(FSem, 0);
- end;
Using this class I can run a procedure of object with this parameter using TWorkerThreadList object:
TWorkEvent = procedure(WT: TWorkerThread) of object;
How to use the TWorkerThreadList class:
- Define WorkerThreads variable as global variable or in the data module:
WorkerThreads: TWorkerThreadList; - Add object create and free for the WorkerThreads object in the data module on create and on destroy:
- procedure TDaemon1.DataModuleCreate(Sender: TObject);
- begin
- WorkerThreads := TWorkerThreadList.Create;
- end;
- procedure TDaemon1.DataModuleDestroy(Sender: TObject);
- begin
- WorkerThreads.Free;
- end;
- Create the thread procedure:
- procedure TDaemon1.WorkServerListener(Thread: TWorkerThread);
- begin
- try
- { do something }
- except
- on E: Exception do
- DebugLog('Daemon process terminated by error '+E.ClassName+': '+
- E.Message);
- end;
- end;
- Create thread for the procedure:
- procedure TDaemon1.DataModuleStart(Sender: TCustomDaemon; var OK: Boolean
- );
- begin
- WorkerThreads.AddNew(@WorkServerListener, nil);
- OK := True;
- end;
WorkerThreadList object can be created and destroyed as needed. A WorkerThreadList object can run alot of procedures.
Working with thread can be tricky, You have to be carefull with race condition, some objects that is not thread safe you need to put semaphore so that only one thread can access it at a time. Memory leak is not the only bugs.
Thats all for the WorkerThreadList object, and I hope this article can be useful.

Tidak ada komentar:
Posting Komentar