The app is written in Delphi XE.
I have two classes, a TBoss and TWorker, which are both based of of TThread. The TBoss is a single instance thread, which starts up and then will create about 20 TWorker threads.
When the boss creates a instance of TWorker it assigns it a method to call synchronize on, when the Worker has finished with what it's doing it calls this method which allows the Boss to access a record on the Worker.
However I feel this is a problem, calling synchronize appears to be locking up the whole application - blocking the main (ui) thread. Really it should just be synchronizing that worker to the boss thread....
Previously I used messages/packed records to send content between threads which worked well. However doing it this way is much cleaner and nicer.... just very blocking.
Is there a way to call Syncronize in the worker to only wait for the Boss thread?
My code:
type
TWorker = class(TThread)
private
fResult : TResultRecord;
procedure SetOnSendResult(const Value: TNotifyEvent);
....
....
public
property OnSendResult: TNotifyEvent write SetOnSendResult;
property Result : TResultRecord read fResult;
....
end;
...
...
procedure TWorker.SendBossResults;
begin
if (Terminated = False) then
begin
Synchronize(SendResult);
end;
end;
procedure TWorker.SendResult;
begin
if (Terminated = false) and Assigned(FOnSendResult) then
begin
FOnSendResult(Self);
end;
end;
Then in my Boss thread I will do something like this
var
Worker : TWorker;
begin
Worker := TWorker.Create;
Worker.OnTerminate := OnWorkerThreadTerminate;
Worker.OnSendResult := ProcessWorkerResults;
So my boss then has a method called ProcessWorkerResults - this is what gets run on the Synchronize(SendResult); of the worker.
procedure TBoss.ProcessWorkerResults(Sender: TObject);
begin
if terminated = false then
begin
If TWorker(Sender).Result.HasRecord then
begin
fResults.Add(TWorker(Sender).Result.Items);
end;
end;
end;
Synchronize
is specifically designed to execute code in the main thread; that's why it seems to lock everything up.
You can use several ways to communicate from the worker threads to the boss thread:
Add a callback to each worker thread, and assign it from the boss thread when it's created. It can pass back whatever as parameters, along with a thread ID or some other identifier.
Post a message from the worker thread to the boss thread using PostThreadMessage. The disadvantage here is that the boss thread has to have a window handle (see Classes.AllocateHWnd in the Delphi help and David Heffernan's comment below).
Use a good quality third-party threading library. See OmniThreadLibrary - it's free, OS, and extremely well written.
My choice would be the third. Primoz has done all the hard work for you. :)
After your comment, here's something along the lines of my first suggestion. Note that this is untested, since writing the code for a TBoss and TWorker thread + a test app is a little long for the time I have right this minute... It should be enough to give you the gist, I hope.
type
TWorker = class(TThread)
private
fResult : TResultRecord;
fListIndex: Integer;
procedure SetOnSendResult(const Value: TNotifyEvent);
....
....
public
property OnSendResult: TNotifyEvent write SetOnSendResult;
property Result : TResultRecord read fResult;
property ListIndex: Integer read FListIndex write FListIndex;
....
end;
type
TBoss=class(TThread)
private
FWorkerList: TThreadList; // Create in TBoss.Create, free in TBoss.Free
...
end;
procedure TWorker.SendBossResults;
begin
if not Terminated then
SendResult;
end;
procedure TBoss.ProcessWorkerResults(Sender: TObject);
var
i: Integer;
begin
if not terminated then
begin
If TWorker(Sender).Result.HasRecord then
begin
FWorkerList.LockList;
try
i := TWorker(Sender).ListIndex;
// Update the appropriate record in the WorkerList
TResultRecord(FWorkerList[i]).Whatever...
finally
FWorkerList.UnlockList;
end;
end;
end;
end;
You could use a thread safe queue. In DelphiXE there is the TThreadedQueue. If you don't have DXE, try OmniThreadLibray - this library is very good for all threading issues.
As I mentioned new options in Delphi 2009 and higher, here is a link to an example for Producer / Consumer communication between threads, based on the new objct locks, in my blog:
Thread Synchronization with Guarded Blocks in Delphi
In a note regarding the deprecated methods TThread.Suspend and TThread.Resume, The Embarcadero DocWiki for Delphi recommends that “thread synchronization techniques should be based on SyncObjs.TEvent and SyncObjs.TMutex.“ There is, however, another synchronization class available since Delphi 2009: TMonitor. It uses the object lock which has been introduced in this version ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With