Accessing Variable in Parent Form from OnTimer Event - Getting Exception - multithreading

I'm getting an exception in an OnTimer event handler (TTimer) that when executed increments an integer variable in the parent form. The timers need to be able to access an incremented integer used as an id.
My first question is: How can I tell in Delphi 2007 which code is running in which thread? Is there a way in debug mode to inspect this so I can determine for sure?
Secondly, if I need to access and modify variables in a parent form from another thread, what is the best way to do that? It seems like sometimes Delphi allows me to access these variables "incorrectly" without giving an exception and other times it does give an exception.

Just to be sure: On one hand you are talking about a timer event, on the other about multithreading. Those are two totally different ways of running code in parallel.
A timer will always be run in the main thread. It should be safe there to access everything that was created and is being used in the main thread. In fact, a timer event can only occur, when no other main thread code is running, because it needs the application's message handler to process the timer message. So it is either outside of any event handling code or when one of your event handlers calls Application.ProcessMessages.
A thread is very different from this. In this case, the code in different threads runs independently from each other. If running on a multi-processor machine (or multi core), it is even possible they truly run in parallel. There are quite a few issues you may have this way, in particular the Delphi VCL (up and including Delphi XE) is not thread save, so calls to any VCL class must only be done from the main thread (there are a few exceptions to this rule).
So, please first clarify whether you are talking about timers or true multithreading, before expecting any useful answers.

How can I tell in Delphi 2007 which
code is running in which thread? Is
there a way in debug mode to inspect
this so I can determine for sure?
You can set a breakpoint and when execution stops look at the threads debug window. Double click on each thread to see its callstack in the callstack debug window. You can also use the Win32 function GetCurrentThreadId to find out about the current thread (e.g. for logging, or to determine if the current thread is the main thread etc).
Since you are not showing any code it is hard to be more specific. Just to be sure: code in a timer event handler is not getting executed in a different thread. You won't have concurrent-access issues if you are just using timers, not real background threads.
Secondly, if I need to access and
modify variables in a parent form from
another thread, what is the best way
to do that? It seems like sometimes
Delphi allows me to access these
variables "incorrectly" without giving
an exception and other times it does
give an exception.
If you really are in another thread and access a shared variable you can see all sorts of things happening if you don't protect that access. It might work ok most of the time or you get strange values. If you just want to modify an integer in a thread-safe manner, look at InterlockedIncrement. Otherwise you could use a critical section, mutex, monitor... JEDI has some useful classes in the JclSynch unit for that.

You are asking two questions, so I'll answer them in two answers.
Your first question is about using TTimers; those always run in the main thread.
Most likely, your exception is an access violation.
If it is, it is usually caused by either of these:
a- your parent form is already
destroyed when your TTimer fires.
b- your do not have a reference yet to
your parent form when your TTimer
fires.
b is easy: just check if your reference is nil.
a is more difficult and depends on how you reference your parent form.
Basically you want to make sure your reference gets nil when the parent is being destroyed or removed.
If you reference your parent form through a global variable (in this example through Form2), then you should have TForm2 make the Form2 variable nil using the OnDestroy event like this:
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm2 = class(TForm)
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.FormDestroy(Sender: TObject);
begin
Form2 := nil;
end;
end.
If you are using a field reference to your parent form (like FMyForm2Reference), then you should use add a Notification method like this:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Unit2;
type
TForm1 = class(TForm)
private
FMyForm2Reference: TForm2;
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) then
if (AComponent = FMyForm2Reference) then
FMyForm2Reference := nil;
end;
end.
Regards,
Jeroen Pluimers

You are asking two questions, so I'll answer them in two answers.
Your second question is about making sure only 1 thread accessing 1 variable in a form at a time.
Since the variable is on a form, the best way is to use the Synchronize method for this.
There is an excellent example about this which that ships with Delphi, it is in the thrddemo.dpr project, where the unit in SortThds.pas has this method that shows how to use it:
procedure TSortThread.VisualSwap(A, B, I, J: Integer);
begin
FA := A;
FB := B;
FI := I;
FJ := J;
Synchronize(DoVisualSwap);
end;
Good luck,
Jeroen Pluimers

Related

How Do I Call a Thread.Execute in GUI Thread?

There is a TThread descendant class with its own Execute method doing some math. It works fine but I am looking for the optimization of the following kind. The GUI thread and context of the program determine the count of necessary instances of these threads to be created, run and freed. In certain (rare or user determined) circumstances creation of one instance is enough.
By the moment I use the following construction (in pseudocode):
if ThreadsCount>1 then
begin
Creation of threads
starting them
waiting for results
evaluating and assigning the best result
freeing the threads
end
else
starting the math procedure (edited here separately)
// and in MyThread class declaration
procedure Execute... (edited here separately)
So there are two places in code that have my math procedure and I have to edit both of them if some math changes are applied. The GUI math procedure is a bit different from that one called in thread so I can not simply extract the method and call it.
I wonder if there is a way to create a thread instance and call its Execute method in GUI thread?
You could write some seriously hacky, indescribably bad code to enable you to safely call a TThread's Execute(). But it's an absurd thing to do. The whole point of the TThread class is that it:
starts a new thread in the OS;
then calls Execute() on that thread.
So:
If you don't need a thread, there's absolutely no point in starting a thread that you don't want to use.
You would need to prevent Execute() from doing any processing on its thread-run.
You could then call Execute from the main thread.
But since you have no guarantees how long the thread will take to not do any processing when it calls Execute(), you'd still need to wait for the thread to finish before you can destroy the TThread object.
The GUI math procedure is a bit different from that one called in thread so I can not simply extract the method and call it.
This makes absolutely no sense.
If your two "math procedures" are different, then trying to call the thread-implementation from GUI would change the behaviour of your program. Conversely, if you can reuse the thread-implementation, then you most certainly can also extract the method! (Or at the very least the common elements.)
Caution
That said, there is some caution required when sharing code that might run in a TThread.Execute(). Any code that must run on the main thread needs to be synchronised or queued. Inside TThread objects, you'd simply call the Synchronize() or Queue() method. However, shared code shouldn't be on a TThread object making things a little trickier.
To resolve this, you can use the the Synchronize() and Queue() class methods on TThread. This allows you to synchronise without instantiating a TThread instance. (Note these methods are safe to call from the main thread because they would simply call the sync method directly in that case.)
Code along the following lines should do the trick.
Implement your shared code in a suitable object. This is conceptually a runnable object, and something you may want to research.
TSharedProcess = class
private
{ Set this if the process is run from a child thread,
leave nil if run from main thread. }
FThread: TThread;
procedure SyncProc();
public
procedure Run();
property Thread: TThread read FThread write FThread;
end;
procedure TSharedProcess.Run();
begin
...
TThread.Synchronize(FThread, SyncProc);
...
end;
When you want to run the shared code from the main thread, the following is an option.
begin
LProc := TSharedProcess.Create(...);
try
LProc.Run();
finally
LProc.Free;
end;
end;
To run from a child thread a simple thread wrapper will suffice. And then you can create the runnable object in the main thread, and pass it to the thread wrapper.
{ TShardProcessThread for use when calling from child thread. }
constructor TSharedProcessThread.Create(AProc: TSharedProcessThread);
begin
FProc := AProc;
FProc.Thread := Self;
inherited;
end;
procedure TShardProcessThread.Execute();
begin
FProc.Run();
end;
{ Main thread creates child thread }
begin
{ Keep reference to FProc because it can only be destroyed after
thread terminates.
TIP: Safest would be to use a reference counted interface. }
FProc := TSharedProcess.Create(...);
try
LThread := TShardProcessThread.Create(FProc);
LThread.OnTerminate := HandleThreadTerminate;
except
{ Exception in thread create means thread will not run and
will not terminate; so free object immediately. }
FProc.Free;
raise;
end;
end;
Disclaimer
I have not tested this code because I see no benefit in doing something like this. Users gain nothing by being able to force code to run on the main thread. Furthermore the paradigms for synchronous code are fundamentally different to asynchronous code. Trying to implement a hybrid reduces maintainability by cluttering your 'business code' with technical detail.
Use at your own risk.
The way to approach this problem is to extract into a method the code that you need to perform either in a worker thread or the main thread. You can then call that code either from your worker thread's Execute method, or from your main thread code.

How to prevent my threads from exiting before their work is done?

I have 10 threads working together. After starting the threads, 15 seconds later all threads exit before the job done, and only one thread remains.
My code:
procedure TForm1.Button2Click(Sender: TObject);
begin
AA;
BB;
CC;
DD;
EE;
FF;
GG;
HH;
II;
JJ;
end;
procedure TForm1.AA; //same procedure for BB,CC,DD,EE.FF,JJ,HH,II,JJ
begin
lHTTP := TIdHTTP.Create(nil);
TTask.Create(Procedure
try
//HTTP Opertations
finally
end;
end).Start;
end;
Note, i can't Free the HTTP component because if i did i get an AV and I don't know how to debug it, where to correctly free it in the code? However without freeing it the code works well but the threads exit. It might be the problem as Mr Dodge said.
Based on how I see you're creating the TIdHTTP component, it's simply wrong. You shouldn't create an object outside of the thread, then use it from inside the thread. That's not thread-safe. You should create it in the same thread as where it's being used. This is why you're unable to free it as well, so you actually have two problems to fix here at the same time.
I also realized that your lHTTP variable is not in the scope of your code, so I'm going to assume that you have it declared in some global (or otherwise shared) location. Each thread needs its own variable for its own instance.
So your code should look a little more like this:
procedure TForm1.AA; //same procedure for BB,CC,DD,EE.FF,JJ,HH,II,JJ
begin
TTask.Create(Procedure
var
lHTTP: TIdHTTP;
begin
lHTTP := TIdHTTP.Create(nil);
try
//HTTP Opertations
finally
lHTTP.Free;
end;
end).Start;
end;
Other components (such as TADOConnection) would even completely fail and crash for attempting such a thing (since such components utilize COM). Luckily, TIdHTTP does not use COM, but the design is still flawed for the same reason.
Now, when you say that you debugged it, I'm guessing you mean you debugged the code in the actual thread, but the breakpoint jumped to another place in your code before it reached the end of this? That is to be expected when using the debugger in threads. You can't just step into a thread and expect each sequential breakpoint to be in the same thread - I mean, if you have more than one breakpoint in different threads, your debugger is very likely to jump from one to another - because, again, they are multiple threads. I suggest creating some sort of work log, and each thread reports its status and position.
It is literally just like an alternate universe. Multiple different similar threads doing slightly different things than each other. The Delphi Debugger is simply the Time Lord who can see into all the alternate universes.

Delphi and threads: "System Error. Code: 1400. Invalid window handle"

Being kinda new to threading I'm running into an issue:
I've built a small wrapper for Synapse THTTPSend object to handle Async calls through means of a thread. All seems to go well until I exit the application and get this error (using madExcept exceptions handler) "System Error. Code: 1400. Invalid window handle."
main thread ($2d00):
0047f931 +091 x.exe System.SysUtils RaiseLastOSError
0047f88e +00e x.exe System.SysUtils RaiseLastOSError
006198c4 +064 x.exe Vcl.Controls TWinControl.DestroyWindowHandle
0061674c +0dc x.exe Vcl.Controls TWinControl.Destroy
0067487b +05b x.exe Vcl.ComCtrls TTabSheet.Destroy
00616781 +111 x.exe Vcl.Controls TWinControl.Destroy
00673218 +0b8 x.exe Vcl.ComCtrls TCustomTabControl.Destroy
0067529c +06c x.exe Vcl.ComCtrls TPageControl.Destroy
00616781 +111 x.exe Vcl.Controls TWinControl.Destroy
0073d95e +06e x.exe Vcl.Forms TScrollingWinControl.Destroy
0073f5d2 +1e2 x.exe Vcl.Forms TCustomForm.Destroy
0040b2d5 +015 x.exe System TObject.Free
005a034e +08e x.exe System.Classes TComponent.DestroyComponents
0073be06 +046 x.exe Vcl.Forms DoneApplication
00472520 +030 x.exe System.SysUtils DoExitProc
0040e0d9 +079 x.exe System #Halt0
I've tracked this down to accessing a listview, it goes like this:
GUI calls a proc in my wrapper and assigns a callback method
Wrapper creates a thread and sets a callback
Thread does its job (http post) then calls the wrapper's callback
Wrapper's callback triggers another callback in the GUI which then updates some items in a listview
If I skip that listview part the error never happens, so I think something may be wrong in my thread code that messes up with the vcl/gui, probably cause it's still running while the VCL is being accessed? If I check the listview there is something very odd with it after the thread ends, sometimes the listview isn't even visible, or the added items aren't clickable.
Listview part
procedure Tx.AddLog(url,DelURL: string);
begin
if Settings.OptEnableLogging.Checked then begin
With UploadsForm.ListView1.Items.Add do begin
Caption := DateTimeToStr(Now);
SubItems.Add(OriginalFilename);
SubItems.Add(url);
SubItems.Add('');
SubItems.Add(DelURL);
end;
SaveLoggingLog;
end;
With UploadsForm.ListView2.Items.Add do begin
Caption := DateTimeToStr(Now);
SubItems.Add(OriginalFilename);
SubItems.Add(url);
SubItems.Add('');
SubItems.Add(DelURL);
end;
end;
The thread object
type
TMySynHTTPAsync = class(TThread)
protected
procedure Execute; override;
private
sObj: TSynHTTP;
public
Constructor Create(SynObj: TSynHTTP);
Destructor Destroy; override;
end;
implementation
Constructor TMySynHTTPAsync.Create(SynObj: TSynHTTP);
begin
inherited Create(False);
Self.FreeOnTerminate := True;
sObj := SynObj;
end;
Destructor TMySynHTTPAsync.Destroy;
begin
//
inherited Destroy;
end;
Procedure TMySynHTTPAsync.Execute;
begin
With sObj do begin
try
case tCallType of
thPostString: ThreadResult := sObj.Post(tURL, tPostVars);
end;
except
//
end;
if Assigned(sObj.xOnAsyncRequestDone) then sObj.xOnAsyncRequestDone;
FThread := nil;
end;
end;
creating the thread
FThread: TThread;
procedure TSynHTTP.DoAsync;
begin
ThreadResult := False;
FThread := TMySynHTTPAsync.Create(Self);
FThread.Resume;
end;
I'm guessing this is the culprit, as it goes through all the GUI processing before the thread finishes.
if Assigned(sObj.xOnAsyncRequestDone) then sObj.xOnAsyncRequestDone;
How could I solve this?
You have posted a lot of code but not the key, relevant part. Specifically the implementation of your xOnAsyncRequestDone event handler/method (unless it literally only calls that log method that you posted).
This method is being executed in the context of the TMySynHTTPAsync thread and based on the behaviour you are describing - particularly the fact that Synchronize resolves your problem - it is highly likely that some activity in that event handler is creating a window handle.
That window handle is then owned by the HTTP Async thread, not the main application thread (sometimes referred to as the "VCL thread") that is otherwise running your application. When your application closes, the VCL thread performs some final housekeeping, destroying objects and windows etc. if one of those windows was created by some other thread this will cause problems.
Window handles are the strict property of the thread in which they were created. You cannot create a window handle in one thread and then destroy it in another.
NOTE: This is a fundamental of Windows, not Delphi.
Worth noting here is that window handles in VCL can often be created indirectly. You won't necessarily see an explicit creation of a control that marks the creation of the underlying window handle. It is quite common for window handles only to be actually created when required. Similarly changing the properties of a control can trigger the VCL to attempt to recreate the window for that control, destroying the current one in the process.
It should be fairly apparent that these mechanisms are highly vulnerable to problems that can arise when VCL methods are called by threads other than the VCL thread. This is why you will often here it said that "the VCL is not thread-safe".
The safest way to operate is to only manipulate VCL objects from code running in the VCL thread itself.
Synchronize to the Rescue
This is in fact precisely why Synchronize exists.
The mechanism that you are invoking by using Synchronize actually works to ensure that the method you are Synchronizing is execute on the VCL thread. If this is in fact creating a window handle then when the VCL thread later comes to destroy that window handle it is quite free to do so since it did in fact create it.
Hence your problem is solved.
Other Options
The Synchronize mechanism is quite complex however, dealing (these days) with cross platform concerns among other things, and as a result may be overkill in this case.
If your code is specific to Windows, a possible alternate solution to this problem may be to exploit the fact that windows allows threads to send (or post) messages to windows in other threads. When those messages are received by those windows, they are then processed by that window's own thread just as all other messages to those windows are. i.e. you cannot end up interrupting a "click" message received by that window by suddenly jumping across to run the notification from the thread. That notification message simply has to wait it's turn while the window finishes processing that click message. For example.
You can think of this as a 'Synchronize' system "built-in" in to the OS.
So you could, for example, pass a window handle to a form (or control or anything with a window handle) to your HTTP async thread during initialisation, identifying a VCL window that wishes to receive the "request complete" or other notifications from the thread. The thread can then send notifications to that window handle using PostMessage or SendMessage which you could handle either by overriding the WindowProc on the form or using a declared message handler.
If the thread uses SendMessage() to send the notification, then it is automatically suspended and forced to wait until the message is received and processed by the window (in the VCL thread).
If the thread uses PostMessage() then the message is sent asynchronously and the thread can continue with other work without having to wait. The VCL thread will eventually pick up the message and process it.
NOT a Recommendation
This is not to say that I would recommend this alternative in this case. Although it does seem that it might be appropriate given that it does appear to be a simple "work is complete" notification in this case, without a more comprehensive understanding of your specific needs it is impossible to say which is most appropriate.
I mention it only to highlight the fact that alternatives do exist and that the key to safe, reliable threading is to understand the principles and the mechanisms involved.
The golden rule with threading is to not touch the GUI from another thread.
Depending on the situation this can be solved with Synchronize(), posting messages async (PostMessage()) or synchronized (SendMessage()). Another asynchronic option is using the TThread.Queue() call.
Last but not least, if you want to notify the GUI that the thread is done, assign an OnTerminate event handler to the thread. This event is executed in the main thread when the thread finishes executing.
This is an example how it could be implemented:
type
TMySynHTTPAsync = class(TThread)
protected
procedure Execute; override;
private
sObj: TSynHTTP;
procedure MyTerminateHandler(Sender: TObject);
public
Constructor Create(SynObj: TSynHTTP);
Destructor Destroy; override;
end;
procedure TMySynHTTPAsync.MyTerminateHandler(Sender: TObject);
begin // Executed in the main thread
if Assigned(sObj) and Assigned(sObj.xOnRequestDone) then sObj.xOnRequestDone;
end;
procedure TMySynHTTPAsync.Execute;
begin
Self.OnTerminate := MyTerminateHandler; // Assign the OnTerminate event handler
...
end;
Synchronize(sObj.xOnAsyncRequestDone) seems to solve the issue.

Safely building a custom thread as base for descendants

I'm writing a custom thread which includes some added functionality. The part I'm confused about is how to handle the Execute procedure, while still expecting it to be descended into more inherited implementations.
My custom thread is overriding the Execute procedure and adding some of my own stuff, such as events OnStart, OnStop and OnException, as well as looping capabilities. I'm not sure how to design this in a way that expects it to be further used in a further inherited thread.
How do I make it possible to further inherit this custom thread while maintaining the Execute functionality?
Here's the execute procedure as I have overridden it...
procedure TJDThread.Execute;
begin
Startup;
try
while not Terminated do begin
if assigned(FOnExecute) then
FOnExecute(Self);
if not FRepeatExec then
Terminate
else
if FExecDelay > 0 then
Sleep(FExecDelay);
end;
finally
Cleanup;
end;
end;
I'm intending for FOnExecute to be actually an event of the thread, which is more-so a replacement of inheriting the Execute procedure - similar to how a service works. I don't think this is the right way to go... How do I make sure this is coded in a safe manner? I'm open to suggestions to another approach than an event - so long as it's aimed at the goal of making a custom TThread which can be inherited and further executed.
This custom thread I'm making includes some additional capabilities which don't come with the original TThread and yet will be extremely useful for many future projects. The additional capabilities are specifically OnStart and OnStop events (similar to how a service works), CoInitialize built in (and only used if told to, default = false), Repeated execution (default = false), and delay between executions (default = 0).
I agree with Rob. Don't use an event, use a virtual method. But even if you were to use the event and employ its "assignedness" to signal whether there is work to be done, you would need to protect the FOnExecute member as it can be set from different threads.
In one of our thread classes we use commands to do something similar:
procedure TCommandThread.SetCommand(const Value: ICommand);
begin
Lock;
try
Assert(not IsAvailable, 'Command should only be set AFTER the thread has been claimed for processing');
FCommand := Value;
if Assigned(FCommand) then
MyEvent.SetEvent;
finally
Unlock;
end;
end;
As SetCommand (the Command's setter) can be called from any ol' thread, setting the FCommand member is protected by the thread's critical section which is locked and unlocked through the Lock and Unlock methods.
Signalling MyEvent is done because our thread class uses a TEvent member to wait for work.
procedure TCommandThread.Execute;
begin
LogDebug1.SendFmtMsg('%s.Execute : Started', [ClassName]);
// keep running until we're terminated
while not Terminated do
try
// wait until we're terminated or cleared for take-off by the threadpool
if WaitForNewCommand then
if Assigned(FCommand)
and not Terminated then
// process the command if we're told to do so
CommandExecute;
except
LogGeneral.SendFmtError('%s.Execute : Exception occurred :', [ClassName]);
LogGeneral.SendException;
end;
LogDebug1.SendFmtMsg('%s.Execute : Finished', [ClassName]);
end;
WaitForNewCommand returns when the MyEvent is signalled. This is done when a command is assigned, but also when a (running) command is cancelled, when the thread is terminated etc. Note that Terminated is checked again just before CommandExecute is called. This is done because when WaitForNewCommand returns, we could be in a situation where both a command was assigned and terminate has been called. After all, signalling the event can be done twice from different threads and we don't know when or in what order anything happened.
CommandExecute is a virtual method that different thread classes can override. In the default implementation it provides for all the status processing around command execution so the commands themselves can concentrate on their own stuff.
procedure TCommandThread.CommandExecute;
var
ExceptionMessage: string;
begin
Assert(Assigned(FCommand), 'A nil command was passed to a command handler thread.');
Assert(Status = chsIdle, 'Attempted to execute non-idle command handler thread');
// check if the thread is ready for processing
if IsAvailable then // if the thread is available, there is nothing to do...
Exit;
try
FStatus := chsInitializing;
InitializeCommand;
FStatus := chsProcessing;
try
ExceptionMessage := '';
CallCommandExecute;
except
on E: Exception do begin
ExceptionMessage := E.Message;
LogGeneral.SendFmtError('%s.CommandExecute: Exception occurred during commandhandler thread execution:', [ClassName]);
LogGeneral.SendException;
end;
end;
finally
FStatus := chsFinalizing;
FinalizeCommand;
FStatus := chsIdle;
FCommand := nil;
// Notify threadpool we're done, so it can terminate this thread if necessary :
DoThreadFinished;
// Counterpart to ClaimThreadForProcessing which is checked in IsAvailable.
ReleaseThreadForProcessing;
end;
end;
CallCommandExecute is where, through several levels of indirection the FCommand's Execute method is called and where the real work of the command is done. That is why that call is directly protected with a try-except block. Other than that each Command in and of itself is responsible for thread safety with regard to the resources it uses.
ClaimThreadForProcessing and ReleaseThreadForProcessing are used to claim and release a thread. For speed's sake they don't use the thread's lock, but use the interlocked mechanism to change the value of the class' FIsAvailable member which is declared as a pointer and used as a boolean:
TCommandThread = class(TThread)
// ...
FIsAvailable: Pointer;
function TCommandThread.ClaimThreadForProcessing: Boolean;
begin
Result := Boolean(CompatibleInterlockedCompareExchange(FIsAvailable, Pointer(False), Pointer(True)));
// (See InterlockedExchange help.)
end;
function TCommandThread.ReleaseThreadForProcessing: Boolean;
begin
FIsAvailable := Pointer(True);
Result := IsAvailable;
end;
If any of the "finally" processing in the CommandExecute method needs to be done regardless of exceptions raised by other calls in that process, you will have to use nested try-finally's to ensure that is the case. The above method was simplified from our real code and the actual finally block is a set of nested try finally's to ensure that DoThreadFinished etc. get called regardless of exceptions in FinalizeCommand (and other calls in between).
Don't worry about how to make it safe to override Execute. Consumers who override your thread's Execute method won't work correctly (because they'll put their own operations around your bookkeeping code instead of within it). Provide a new virtual method for descendants to call instead. You could call it Run, for example, using Indy's TIdThread as a guide. It does much of the same things you're planning on.
Don't call Sleep(FExecDelay) - it's a kernel call that the descendant may not wish to make, so:
if (FExecDelay<>0) then Sleep(FExecDelay);
This gives a user the choice of avoiding the kernel call entirely.
I have issues with TThread.Synchronize - I would not want to force any user to have to call it.
TBH, I'm more used to putting code into an object class that is not descended from TThread, ie. a 'Ttask' that has a 'work' method that is called from the TThread. Having a separate class for the work is hugely more flexible and safer than adding data members and methods to a TThread descendant - it's easily queued in, queued out, PostMessaged etc. That, and not having access to the TThread instance stops developers using TThread.Synchronize, TThread.WaitFor and TThread.OnTerminate, so increasing the reliability and performance of the app.

Synchronizing/sending data between threads

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 ...
public properties of the TWorker class MUST have get and set methods, so you can use a Tcriticalsection to give the values of the properties. Otherwise, you´d be having thread-safe issues. Your example seems ok, but in the real world, with thousands of threads accessing to the same value would result in an read error. Use critical sections.. and you wouldn´t have to use any Synchronize. This way you avoid going to the message queues of windows and improve performance. Besides, if you use this code in a windows service app, (where windows messages aren´t allowed), this example wouldn´t work. The synchronize method doesn´t work unless there´s access to the windows message queue.
Solved!! (answer taken from the question)
The fixes made for this problem where two fold.
First remove the syncronization call in the TWorker SendBossResult method.
Second add a fProcessWorkerResult CritialSection to TBoss class. Create and Free this in create/destroy of the TBoss. In the ProcessWorkerResults method call fProcessWorkerResult.Enter and fProcessWorkerResult.leave around the code which needs to be safe from multiple worker results streaming in.
The above was the conclusion after Kens code and follow up comment. Many thanks kind sir, hats off to you!.

Resources