I have a small client-server application, where server sends some messages to the client using named pipes. The client has two threads - main GUI thread and one "receiving thread", that keeps receiving the messages sent by server via the named pipe. Now whenever some message is received, I'd like to fire a custom event - however, that event should be handled not on the calling thread, but on the main GUI thread - and I don't know how to do it (and whether it's even possible).
Here's what I have so far:
tMyMessage = record
mode: byte;
//...some other fields...
end;
TMsgRcvdEvent = procedure(Sender: TObject; Msg: tMyMessage) of object;
TReceivingThread = class(TThread)
private
FOnMsgRcvd: TMsgRcvdEvent;
//...some other members, not important here...
protected
procedure MsgRcvd(Msg: tMyMessage); dynamic;
procedure Execute; override;
public
property OnMsgRcvd: TMsgRcvdEvent read FOnMsgRcvd write FOnMsgRcvd;
//...some other methods, not important here...
end;
procedure TReceivingThread.MsgRcvd(Msg: tMyMessage);
begin
if Assigned(FOnMsgRcvd) then FOnMsgRcvd(self, Msg);
end;
procedure TReceivingThread.Execute;
var Msg: tMyMessage
begin
//.....
while not Terminated do begin //main thread loop
//.....
if (msgReceived) then begin
//message was received and now is contained in Msg variable
//fire OnMsgRcvdEvent and pass it the received message as parameter
MsgRcvd(Msg);
end;
//.....
end; //end main thread loop
//.....
end;
Now I'd like to be able to create event handler as member of TForm1 class, for example
procedure TForm1.MessageReceived(Sender: TObject; Msg: tMyMessage);
begin
//some code
end;
that wouldn't be executed in the receiving thread, but in main UI thread. I'd especially like the receiving thread to just fire the event and continue in the execution without waiting for the return of event handler method (basically I'd need something like .NET Control.BeginInvoke method)
I'm really beginner at this (I tried to learn how to define custom events just few hours ago.), so I don't know if it's even possible or if I'm doing something wrong, so thanks a lot in advance for your help.
You've had some answers already, but none of them mentioned the troubling part of your question:
tMyMessage = record
mode: byte;
//...some other fields...
end;
Please take note that you can't do all the things you may take for granted in a .NET environment when you use Delphi or some other wrapper for native Windows message handling. You may expect to be able to pass random data structures to an event handler, but that won't work. The reason is the need for memory management.
In .NET you can be sure that data structures that are no longer referenced from anywhere will be disposed off by the garbage collection. In Delphi you don't have the same kind of leeway, you will need to make sure that any allocated block of memory is also freed correctly.
In Windows a message receiver is either a window handle (a HWND) which you SendMessage() or PostMessage() to, or it is a thread which you PostThreadMessage() to. In both cases a message can carry only two data members, which are both of machine word width, the first of type WPARAM, the second of type LPARAM). You can not simply send or post any random record as a message parameter.
All the message record types Delphi uses have basically the same structure, which maps to the data size limitation above.
If you want to send data to another thread which consists of more than two 32 bit sized variables, then things get tricky. Due to the size limits of the values that can be sent you may not be able to send the whole record, but only its address. To do that you would dynamically allocate a data structure in the sending thread, pass the address as one of the message parameters, and reinterpret the same parameter in the receiving thread as the address of a variable with the same type, then consume the data in the record, and free the dynamically allocated memory structure.
So depending on the amount of data you need to send to your event handler you may need to change your tMyMessage record. This can be made to work, but it's more difficult than necessary because type checking is not available for your event data.
I'd suggest to tackle this a bit differently. You know what data you need to pass from the worker threads to the GUI thread. Simply create a queueing data structure that you put your event parameter data into instead of sending them with the message directly. Make this queue thread-safe, i.e. protect it with a critical section so that adding or removing from the queue is safe even when attempted simultaneously from different threads.
To request a new event handling, simply add the data to your queue. Only post a message to the receiving thread when the first data element is added to a previously empty queue. The receiving thread should then receive and process the message, and continue to pop data elements from the queue and call the matching event handlers until the queue is empty again. For best performance the queue should be locked as shortly as possible, and it should definitely be unlocked again temporarily while the event handler is called.
You should use PostMessage (asynch) or SendMessage (synch) API to send a message to' a window. You could use also some kind of "queue" or use the fantastic OmniThreadLibrary to' do this (highly recomended)
Declare a private member
FRecievedMessage: TMyMEssage
And a protected procedure
procedure PostRecievedMessage;
begin
if Assigned(FOnMsgRcvd) then FOnMsgRcvd(self, FRecievedMessage);
FRecievedMessage := nil;
end;
And change the code in the loop to
if (msgReceived) then begin
//message was received and now is contained in Msg variable
//fire OnMsgRcvdEvent and pass it the received message as parameter
FRecievedMessage := Msg;
Synchronize(PostRecievedMessage);
end;
If you want to do it completely asynch use PostMessage API instead.
Check docs for Synchronize method. It's designed for tasks like yours.
My framework does can do this for you if you wish to check it out (http://www.csinnovations.com/framework_overview.htm).
Related
I am working on small monitoring application which will have some threads for communication with some devices via SNMP, TCP, ICMP, other threads have to perform some calculations.
All this result I have to output in GUI (some Forms or TabSheets).
I am thinking about next possibilities:
use Synchronize from every worker thread:
use shared buffer and windows messaging mechanism. Thread will put message in shared buffer (queue) and will notify GUI with windows message.
use separate thread which will listen for Synchronization primitives (Events, Semaphores, etc) and use again Synchronize, but only from GUI-dedicated thread only, or Critical Section on GUI to display message.
UPDATE: (Proposed by one workmate) use shared buffer and TTimer in main form which will check periodically (100-1000 ms) shared buffer and consuming, instead of windows messaging. (Does it have some benefit over messaging?)
Other?
Dear experts, please explain what is the best practice or what are the advantages and disadvantages of exposed alternatives.
UPDATE:
As idea:
//shared buffer + send message variant
LogEvent global function will be called from everywhere (from worker threads too):
procedure LogEvent(S: String);
var
liEvent: IEventMsg;
begin
liEvent := TEventMsg.Create; //Interfaced object
with liEvent do
begin
Severity := llDebug;
EventType := 'General';
Source := 'Application';
Description := S;
end;
MainForm.AddEvent(liEvent); //Invoke main form directly
end;
In Main Form, where Events ListView and shared section (fEventList: TTInterfaceList which is already thread-safe) we'll be:
procedure TMainForm.AddEvent(aEvt: IEventMsg);
begin
fEventList.Add(aEvt);
PostMessage(Self.Handle, WM_EVENT_ADDED, 0, 0);
end;
Message handler:
procedure WMEventAdded(var Message: TMessage); message WM_EVENT_ADDED;
...
procedure TMainForm.WMEventAdded(var Message: TMessage);
var
liEvt: IEventMsg;
ListItem: TListItem;
begin
fEventList.Lock;
try
while fEventList.Count > 0 do
begin
liEvt := IEventMsg(fEventList.First);
fEventList.Delete(0);
with lvEvents do //TListView
begin
ListItem := Items.Add;
ListItem.Caption := SeverityNames[liEvt.Severity];
ListItem.SubItems.Add(DateTimeToStr(now));
ListItem.SubItems.Add(liEvt.EventType);
ListItem.SubItems.Add(liEvt.Source);
ListItem.SubItems.Add(liEvt.Description);
end;
end;
finally
fEventList.UnLock;
end;
end;
Is there something bad? Main Form is allocated ONCE on application startup and Destroyed on application exit.
Use Synchronize from every worker thread
This would probably be the simplest approach to implement, but as others have indicated will lead to your IO threads being blocked. This may/may not be a problem in your particular application.
However it should be noted that there are other reasons to avoid blocking. Blocking can make performance profiling a little trickier because it effectively pushes up the time spent in routines that are "hurrying up and waiting".
Use shared buffer and windows messaging mechanism
This is a good approach with a few special considerations.
If your data is extremely small, PostMessage can pack it all into the parameters of the message making it ideal.
However, since you make mention of a shared buffer, it seems you might have a bit more data. This is where you have to be a little careful. Using a "shared buffer" in the intuitive sense can expose you to race conditions (but I'll cover this in more detail later).
The better approach is to create a message object and pass ownership of the object to the GUI.
Create a new object containing all the details required for the GUI to update.
Pass the reference to this object via the additional parameters in PostMessage.
When the GUI finishes processing the message it is responsible for destroying it.
This neatly avoids the race conditions.
WARNING: You need to be certain the GUI gets all your messages, otherwise you will have memory leaks. You must check the return value of PostMessage to confirm it was actually sent, and you may as well destroy the object if not sent.
This approach works quite well if the data can be sent in light-weight objects.
Use separate thread ...
Using any kind of separate intermediate thread still requires similar considerations for getting the relevant data to the new thread - which then still has to be passed to the GUI in some way. This would probably only make sense if your application needs to perform aggreagation and time-consuming calculations before updating the GUI. In the same way that you don't want to block your IO threads, you don't want to block your GUI thread.
Use shared buffer and TTimer in main form
I mentioned earlier that the "intuitive idea" of a shared buffer, meaning: "different threads reading and writing at the same time"; exposes you to the risk of race conditions. If in the middle of a write operation you start reading data, then you risk reading data in an inconsistent state. These problems can be a nightmare to debug.
In order to avoid these race conditions you need to fall back on other synchronisation tools such as locks to protect the shared data. Locks of course bring us back to the blocking issues, albeit in a slightly better form. This is because you can control the granularity of the protection desired.
This does have some benefits over messaging:
If your data structures are large and complex, your messages might be inefficient.
You won't need to define a rigorous messaging protocol to cover all update scenarios.
The messaging approach can lead to a duplication of data within the system because the GUI keeps its own copy of the data to avoid race conditions.
There is a way to improve the idea of shared data, only if applicable: Some situations give you the option of using immutable data structures. That is: data structures that do not change after they've been created. (NOTE: The message objects mentioned earlier should be immutable.) The benefit of this is that you can safely read the data (from any number of threads) without any synchronisation primitives - provided you can guarantee the data doesn't change.
The best approach is to use a GDI custom message and just call PostMessage() to notify the GUI.
type
TMyForm = class(TForm)
.
.
.
private
procedure OnMyMessage(var Msg: TMessage); message WM_MY_MESSAGE;
procedure OnAnoMessage(var Msg: TMessage); message WM_ANO_MESSAGE;
.
.
PostMessage(self.Handle,WM_MY_MESSAGE,0,0);
See this great article for full explanation.
This is a lighter/faster approach to rely on the OS internal features.
I'm writing some software that talks to external hardware via a dll (moving some motors and reading some values back). The calls to the dll are blocking and may not return for in the order of 10 seconds. The software performs a scan by moving the hardware, taking a reading and repeating for a number of points. One scan can take in the order of 30 minutes to complete. While the scan is running I would obviously like the GUI to be responsive and a live graph (in an MDI Child) of the incoming data to be updated at each point. Multithreading seems the obvious choice for this problem.
My question is, what is the best way to thread this and talk back to the main VCL thread to update the graph during a scan?
I currently have a single TThread descendant that performs the 'scan logic' and an array of doubles in the public var section of the ChildForm. I need to fill out this array from the thread but I don't know whether to use Synchronize or CriticalSection or PostMessage or some other method. Each time a new value is added, the main VCL thread needs to update the graph. Should I really have an intermediary object for the data that is a global var and access this from the Thread and the ChildForm separately somehow?
The simplest way to update the GUI from a thread is to use anonymous methods in conjunction with TThread.Synchronize and TThread.Queue.
procedure TMyThread.Execute;
begin
...
Synchronize( // Synchronous example
procedure
begin
// Your code executed in main thread here
end
);
...
Queue( // Asynchronous example
procedure
begin
// Your code executed in main thread here
end
);
end;
Passing values asynchronous often requires "capturing" a value.
procedure TMyThread.PassAValue(anInteger: Integer);
begin
Queue(
procedure
begin
// Use anInteger in main thread
end
);
end;
procedure TMyThread.Execute;
var
myInt: Integer;
begin
...
PassAValue(myInt); // Capture myInt
...
end;
When an anonymous method is using a variable, the reference to the variable is captured.
This means that if you alter the variable value before the anonymous method is executed, the new value is used instead. Hence the need to capture the "value".
A more elaborate example can be found here, synchronize-and-queue-with-parameters, by #UweRaabe.
If you want to invest a little more then a simple Synchronize call which by the way blocks the main thread, you can add a simple FIFO queue with messaging on top of it. The flow of data would be like this:
The thread puts the data into the queue.
The thread post a message to the main thread window. Which one I don't care :)
You handle the message that data is available and process any messages in the queue as you see fit.
The code would look something like this:
the queue...
const
WM_DataAvailable = WM_USER + 1;
var
ThreadSafeQueue: TThreadSafeQueue;
the data is put into the queue...
procedure PutDataIntoQueue;
var
MyObject: TMyObject;
begin
MyObject := TMyObject.Create;
ThreadSafeQueue.Enqueue(MyObject);
PostMessage(FMainWindowHandle, WM_DataAvailable, 0, 0);
end;
and processing...
procedure ProcessDataInTheQueue(var Msg: TMessage); message WM_DataAvailable;
procedure ProcessDataInTheQueue(var Msg: TMessage);
var
AnyValue: TAnyValue;
MyObject: TMyObject;
begin
while ThreadSafeQueue.Dequeue(AnyValue) do
begin
MyObject := TMyObject(AnyValue.AsObject);
try
// process the actual object as needed
finally
MyObject.Free
end;
end;
end;
The code is written without Delphi and checks so it can contain errors. I showed the example using my freely available thread safe queue and TAnyValue. You can find both here:
http://www.cromis.net/blog/downloads/
Also please note then I did not do any check if PostMessage was actually sent. You should check that in production code.
I find that populating a TThreadList from the background thread, then posting a message to the main thread that there is a new item in the list, then processing the list in the main thread is simple and easily maintainable.
With this method, you could store as many readings as you wanted in the list, and every time the main thread received a message, it would simply process all the items in the list at once.
Define a class for the readings, instantiate them, and add them to the list in the background thread. Don't forget to free them in the main thread when you pop them off the list.
Use postmessage inside you thread and send messages to main form handle.
Register one (or more) custom messages and write a handler for them.
const WM_MEASURE_MESSAGE = WM_USER + 1;
Create a thread class, add a MainFormHandle property (Thandle or cardinal).
Create thread suspended, set MainFormHandle with main form handle, then resume thread.
When you have a new measure, assign data1 and data2 dword with some data from measure, then
PostMessage(fMainFormHandle,WM_MEASURE_MESSAGE,data1,data2);
In main form you have message handler:
procedure MeasureMessage(var msg: TMessage); message WM_MEASURE_MESSAGE;
begin
// update graph here
// msg.wparam is data1
// msg.lparam is data2
end;
If you need to send much more data from thread to main form, you can create an appropriate structure in main context for the whole measurement data, pass a reference to thread, let the thread write data and use messages just to tell main form new data position (an array index, for example). Use TThread.Waitfor in main context to avoid freeing data structure while thread is still running (and writing into memory).
We have a Delphi client server application using INDY. The client has a single tIdTCPClient connection to the server which is multi threaded. The client is "theoretically" a single thread. But in practice there are multiple threads on the client and this is where my problem is. For example think of a timer that fires every minute to get data from the server. And consider what happens when a user runs a command at the same time as this timer event. In truth, my problem is caused by our "Report Builder" reporting tool that (annoyingly) insists on loading every page of a report, which takes a while. The report runs off our "special" dataset that has a caching mechanism to transmit batches of records at a time (so multiple calls to the server to get all data). Meanwhile if a user does something else at the same time we seem to be getting crossed data. It seems the user getting back data that was meant for the report.
By the way, this bug is extremely rare, but a lot less rare for one particular customer who has the worlds slowest internet (luck me - I now have a test environment).
So on the client I have code a bit like this...
procedure DoCommand(MyIdTCPClient:tIdTCPClient; var DATA:tMemoryStream);
var
Buffer: TBytes;
DataSize: Integer;
CommsVerTest: String;
begin
//Write Data
MyIdTCPClient.IOHandler.Write(DATA.Size);
MyIdTCPClient.IOHandler.Write(RawToBytes(Data.Memory^,DataSize));
//Read back 6 bytes CommsVerTest should always be the same (ie ABC123)
SetLength(Buffer,0); //Clear out buffer
MyIdTCPClient.IOHandler.ReadBytes(Buffer,6);
CommsVerTest:=BytesToString(Buffer);
if CommsVerTest<>'ABC123' then
raise exception.create('Invalid Comms'); //It bugs out here in rare cases
//Get Result Data Back from Server
DataSize:=MyIdTCPClient.IOHandler.ReadLongInt;
Data.SetSize(DataSize); //Report thread is stuck here
MyIdTCPClient.IOHandler.ReadBytes(Buffer,DataSize);
end;
Now when I debug it, I can confirm it bugs out when there are two threads in the middle of this procedure. The main thread stops at the exception. And the report thread is stuck somewhere else in the same procedure.
So, it looks to me like I need to make the procedure above thread safe.
I mean so if the user wants to do something they just have to wait until the report thread finishes.
Arrrgh, I thought my client application was single threaded for sending data to the server!
I think that using TThread would not work - because I don't have access to the thread inside Report Builder. I think I need a tCriticalSection.
I think I need to make the application so that the above procedure can only be run by one thread at a time. Other threads have to wait.
Someone please help with the syntax.
TIdIOHandler has Write() and Read...() overloads for sending/receiving TStream data:
procedure Write(AStream: TStream; ASize: TIdStreamSize = 0; AWriteByteCount: Boolean = False); overload; virtual;
procedure ReadStream(AStream: TStream; AByteCount: TIdStreamSize = -1; AReadUntilDisconnect: Boolean = False); virtual;
You do not need to copy the TMemoryStream contents to an intermediate TIdBytes before sending it, or receive it as TIdBytes before copying it back to the TStream. In fact, there is nothing in in the code you have shown that needs to use TIdBytes directly at all:
procedure DoCommand(MyIdTCPClient: TIdTCPClient; var DATA: TMemoryStream);
var
CommsVerTest: String;
begin
//Write Data
MyIdTCPClient.IOHandler.Write(DATA, 0, True);
//Read back 6 bytes CommsVerTest should always be the same (ie ABC123)
CommsVerTest := MyIdTCPClient.IOHandler.ReadString(6);
if CommsVerTest <> 'ABC123' then
raise exception.create('Invalid Comms');
//Get Result Data Back from Server
DATA.Clear;
MyIdTCPClient.IOHandler.ReadStream(DATA, -1, False);
end;
With that said, if you have multiple threads writing to the same socket at the same time, or multiple threads reading from the same socket at the same time, they will corrupt each other's data (or worse). You need to synchronize access to the socket, such as with a critical section at a minimum. Because of your multi-threaded use of TIdTCPClient, you really need to re-think your overall client design.
At the very least, using your existing logic, when you need to send a command and read a response, stop the timer and wait for any pending data to be exchanged before then sending the command, and do not allow anything else to access the socket until the response comes back. You are trying to do too much at one time without synchronizing everything to avoid overlaps.
In the long run, it would be much safer to do all of the reading from a single dedicated thread and then pass any received data to your other threads for processing as needed. But that also means changing your sending logic to match. You could either:
If your protocol allows you to have multiple commands in flight in parallel, then you can send a command from any thread at any time (just be sure to use a critical section to avoid overlaps), but do not wait for a response immediately. Let each sending thread move on and do other things, and have the reading thread notify the appropriate sending thread asynchronously when the expected response actually arrives.
If the protocol does not allow for parallel commands, but you still need each sending thread to wait for its respective response, then give the socket thread a thread-safe queue that other threads can push commands into when needed. The socket thread can then run through that queue periodically sending each command and receiving its response one at a time as needed. Each thread that puts a command into the queue can include a TEvent to be signaled when the response arrives, that way they enter efficient sleep states while waiting, but you preserve your per-thread waiting logic.
Thanks Remy.
The TCriticalSection solved the problem. I have no control over things like the 3rd party report builder. And running reports entirely in their own thread wouldn't make much difference - they still need to share the same connection (I don't want or need parallel connections). Anyway the bulk of the program runs in the main thread, and it is rare that two threads need to communicate with the server at the same time.
So TCriticalSection was perfect - it prevented this procedure running twice at the same time (ie one thread has to wait until the first is finished). And happily - it worked brilliantly.
Basically the code now looks like this:
procedure DoCommand(
CS:tCriticalSection;
MyIdTCPClient:tIdTCPClient;
var DATA:tMemoryStream);
var
Buffer: TBytes;
DataSize: Integer;
CommsVerTest: String;
begin
CS.Enter; //enter Critical Section
try
//Write Data
MyIdTCPClient.IOHandler.Write(DATA.Size);
MyIdTCPClient.IOHandler.Write(RawToBytes(Data.Memory^,DataSize));
//Read back 6 bytes CommsVerTest should always be the same (ie ABC123)
SetLength(Buffer,0); //Clear out buffer
MyIdTCPClient.IOHandler.ReadBytes(Buffer,6);
CommsVerTest:=BytesToString(Buffer);
if CommsVerTest<>'ABC123' then
raise exception.create('Invalid Comms');
//Get Result Data Back from Server
DataSize:=MyIdTCPClient.IOHandler.ReadLongInt;
Data.SetSize(DataSize);
MyIdTCPClient.IOHandler.ReadBytes(Buffer,DataSize);
finally
cs.Leave;
end;
end;
I'm working on a multi-threaded system which involves the thread notifying its parent of destruction of an object. The problem is, I need to allow that object to remain created long enough for this event to read this object, because I'm passing the object as one of the event's parameters. Currently, when this event is triggered, the object which is passed to the event is already destroyed from within the thread.
I don't want the thread to necessarily wait for this event to be completed, but rather know when this event has been triggered and then destroy the object. I want the code in the thread to continue no matter what, even if the object is still instantiated.
There's a list of these objects, and they're created from within a thread. The thread its self has events for when certain things happen to the objects in this list (specifically in this case the destruction of the object). I'm actually feeding these events into an event queue (a TList which contains record pointers to what event and what object). So somewhere inside the thread, I add a record to this event list.
The thread's execution then comes along and loops through the events in this list and triggers them accordingly (example below). So when the event was added to the list, it saved the object pointer as part of this event's record pointer. There could then be a long delay until the event is actually triggered. At that point, the object needs to still be instantiated so it can be read from outside the thread. Only then shall the object actually be destroyed.
The mechanism used for this event queue doesn't have any room for feedback to the thread. It's already a developed system and any addition for this event queue to tell the thread that the event has been triggered is out of the question, as it would require an entire re-write. Otherwise, I would simply tell the thread to destroy this object once my event has been called.
Here's some snippets, the system is actually very large so it's difficult to show all the functionality. The one thread's event sets off a chain of events through 4 more parent objects, passing this object through each. The goal is to prevent any code outside of the thread to handle this actual destruction. The thread should take full responsibility for waiting for this event before destroying...
type
TJDNetSvrNode = class;
TJDNetSvrThread = class;
TNodeEvent = (neUnload); //And many more
PNodeEventRec = ^TNodeEventRec;
TNodeEventRec = record
Event: TNodeEvent;
Node: TJDNetSvrNode;
end;
TJDNetSvrNodeEvent = procedure(Sender: TObject; Node: TJDNetSvrNode) of object;
TJDNetSvrNode = class(TObject)
//Large object with no relevant members
end;
TJDNetSvrThread = class(TThread)
private
FNodeEvents: TList;
FNodeEvent: PNodeEventRec;
FOnNodeUnload: TJDNetSvrNodeEvent;
procedure SYNC_OnUnload;
public
property OnNodeUnload: TJDNetSvrNodeEvent read FOnNodeUnload write FOnNodeUnload;
end; //Much more in this class
//Starting point of event - adds to event queue list
procedure TJDNetSvrThread.NodeUnloaded(Sender: TObject; Node: TJDNetSvrNode);
var
E: PNodeEventRec;
begin
E:= New(PNodeEventRec);
E.Event:= neUnload;
E.Node:= Node;
FNodeEvents.Add(E);
end;
//Called within thread to execute any events which are queued
procedure TJDNetSvrThread.ProcessNodeEvents;
begin
while FNodeEvents.Count > 0 do begin
FNodeEvent:= PNodeEventRec(FNodeEvents[0]);
FNodeEvents.Delete(0);
case FNodeEvent.Event of
neUnload: begin
Synchronize(SYNC_OnUnload);
end;
//And many more
end;
Dispose(FNodeEvent);
end;
end;
procedure TJDNetSvrThread.SYNC_OnUnload;
begin
if assigned(FOnNodeUnload) then
FOnNodeUnload(Self, FNodeEvent.Node); //Parent also has to use "Node" for its event
//NOW "Node" can be destroyed
end;
Sounds like what you basically want is for an object to be able to be referenced from multiple different places in the system, and not get destroyed until after all the places referencing it are done with it, no matter what order that happens in.
Delphi has a mechanism to do exactly that: reference counting. It's built in to the Interface model. Try turning your object into a TInterfacedObject descendant, create and implement an Interface that exposes the functionality you need, and pass that around instead of the object reference.
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.