When do I need to call CoInitialize() in this scenario? - multithreading

I'm building a multi-threaded windows service application in Delphi XE2 which uses ADO database components to connect to SQL Server. I've used CoInitialize(nil); plenty times before inside threads, but in this case, I have a function which I'm unsure about.
This function is called TryConnect which attempts to connect to a database with a given connection string. It returns true or false on the connection success. The problem is that this function will be used both inside and outside the main service thread, and it will be creating its own temporary TADOConnection component, which requires CoInitialize...
My question is do I need to call CoInitialize inside this function also? If I do, and since the service's execute procedure uses CoInitialize also, will they interfere if I call this function from within the service? The TryConnect function is inside of an object which is created from the main service thread (but will eventually be moved to its own thread). I need to know if calling CoInitialize() twice from the same thread (and CoUninitialize) will interfere - and how to handle this scenario properly.
Here's the code below...
//This is the service app's execute procedure
procedure TJDRMSvr.ServiceExecute(Sender: TService);
begin
try
CoInitialize(nil);
Startup;
try
while not Terminated do begin
DoSomeWork;
ServiceThread.ProcessRequests(False);
end;
finally
Cleanup;
CoUninitialize;
end;
except
on e: exception do begin
PostLog('EXCEPTION in Execute: '+e.Message);
end;
end;
end;
//TryConnect might be called from same service thread and another thread
function TDBPool.TryConnect(const AConnStr: String): Bool;
var
DB: TADOConnection; //Do I need CoInitialize in this function?
begin
Result:= False;
DB:= TADOConnection.Create(nil);
try
DB.LoginPrompt:= False;
DB.ConnectionString:= AConnStr;
try
DB.Connected:= True;
Result:= True;
except
on e: exception do begin
end;
end;
DB.Connected:= False;
finally
DB.Free;
end;
end;
So to clarify what it's really doing, I might have an occasion of this:
CoInitialize(nil);
try
CoInitialize(nil);
try
//Do some ADO work
finally
CoUninitialize;
end;
finally
CoUninitialize;
end;

CoInitialize has to be called in every single thread that uses COM, regardless of what thread it is, or whether it has a parent thread or child threads. If the thread uses COM, it must call CoInitialize.
The correct answer here is "it depends". Since you know the service thread has called CoInitialize, if TryConnect is called from the service thread it won't need to be called again. If the other threads that could call it have also called CoInitialize, it won't need to be called, as the function will run under the calling thread.
The MSDN documentation specifically addresses this question (emphasis added):
Typically, the COM library is initialized on a thread only once. Subsequent calls to CoInitialize or CoInitializeEx on the same thread will succeed, as long as they do not attempt to change the concurrency model, but will return S_FALSE. To close the COM library gracefully, each successful call to CoInitialize or CoInitializeEx, including those that return S_FALSE, must be balanced by a corresponding call to CoUninitialize. However, the first thread in the application that calls CoInitialize with 0 (or CoInitializeEx with COINIT_APARTMENTTHREADED) must be the last thread to call CoUninitialize. Otherwise, subsequent calls to CoInitialize on the STA will fail and the application will not work.
So the answer is: If you're not sure, call CoInitialize. Do it in a try..finally block, and call CoUnitialize in the finally, or initialize in a constructor and uninitialize in the destructor.

Your service thread should really not be doing any work at all. It should be used exclusively for responding to Service Manager calls. The OnExecute or OnStart/OnStop of the service should control the instantiation and execution of a "MainWorkThread" that represents the functionality of your service. See https://stackoverflow.com/a/5748495/11225 for an example.
The main work thread could do the real work and/or delegate it to other threads. Each thread that could use COM should have the CoInitialize/CoUninitialize calls and the easiest way to achieve that is to code them in the outer most try finally block of the thread's (overridden) Execute method.
TDBPool or any other class using COM should not concern itself with the CoInitialize and CoUninitialize calls. These methods need to be called in every thread that could use COM and a class doesn't and shouldn't know in which thread it will be executed.

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.

Delphi TIdTcpServer force stop IdSync after someTimeOut

I need to develop a TCP server and client with persistent connections using Indy and Delphi XE2. Almost everything is going well.
This service is a critical service, so I need to put in some protection in the server to prevent unnecessary processing or freezes. Because of this, I create a thread to check a timeout for critical processes.
I made this TIdSync class:
type
TSync = class(TIdSync)
protected
procedure DoSynchronize; override;
end;
procedure TSync.DoSynchronize;
var
oTimeOut: TThreadTimeOut;
begin
...
oTimeOut := TThreadTimeOut.Create(AContext, WaitTimeOut*2, Self);
oTimeOut.Start;
...
// the code below is just a test, **this is the key to my question**
// if something goes wrong in any subroutine of DoSynchronize, I want
// to stop execution of this object and destroy it. In the thread above
// I want to test when the timeout elapses. If this IdSync object still
// exists and if this routine is still executing, I want to stop execution
// of any routine or subroutine of this object to avoid freezing the
// service and stop memory consumption and CPU usage
while true do begin
Sleep(100);
end;
//If everything is OK
oTimeOut.Stop;
end;
procedure TThreadTimeOut.execute;
var
IniTime: DWORD;
begin
IniTime := GetTickCount;
while GetTickCount < IniTime + TimeOut do begin
Sleep(SleepInterval);
if StopTimeOut then
Exit;
end;
if ((Terminated = False) or (StopTimeOut)) and (IoHandler <> nil) then begin
IOHandler.Connection.IOHandler.Close;
IdSync.Free; //here I try to make things stop execution but the loop to test is still running
end;
end;
This code above works fine to stop receiving and sending data when the timeout elapses, but not to stop execution of TIdSync. How can I do that?
There is no timeout logic in TIdSync (largely because there is no timeout logic in TThread.Synchronize(), which TIdSync uses internally).
You cannot destroy a TIdSync object while it is running. A synced procedure cannot be aborted prematurely once it has been queued for execution, or has started running. It must be allowed to run to completion.
TIdSync.DoSynchronize() (or any method synced with TThread.Queue() or TThread.Synchronize()) is executed in the context of the main UI thread. Long-running code should be executed in its own thread, not in the main UI thread. Make sure the main UI thread is not blocked from processing new messages and sync requests in a timely manner.
If you want to stop a synced procedure, you need to have it handle a TEvent object or other flag which worker threads can signal when needed, and that the procedure checks periodically so it can exit as soon as possible (either gracefully or by raising an exception).
Synched operations of any nature should be short, to prevent blockages/deadlocks, resource starvation, etc. You need to re-think your design. You are doing things the wrong way.

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