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!.
Related
i want to check if Anonymous thread is running,i have an idea which is to monitor the thread status every 1s, if it Exists, restart the work again..
I've got the thread ID, now how to check the status ?
procedure TForm2.Button5Click(Sender: TObject);
begin
TThread.CreateAnonymousThread(procedure ()
var i : integer;
begin
inc(i);
label1.caption := TThread.Current.ThreadID.ToString;
end).Start;
end;
Threads do not just stop. If your thread stops functioning it is because your code has a defect. Even the simple code in the question contains two defects. It reads a local variable before it is initialized and it uses a VCL method away from the main thread.
The entire premise of your question is wrong. You don't need to monitor whether or not your thread is still running. You simply need to fix the defects in your code so that it does not fail.
A better understanding of what threads are and how to use them, will help you. A thread is usually a way to get something done without holding up the user-interface. If you want the user to wait for something to finish, don't use a thread, just put the work code in the buttonclick event handler without any thread creating.
You said
every 1s, if it Exists, restart the work again
That makes no sense. If the thread exists, it's still busy working so there's no need to restart it. Maybe you should tell us what work you want the thread to do exactly.
In the example below (taken from Background Operations on Delphi Android, with Threads and Timers, you can see that the Synchronize procedure is called when the work is done, so that's how you know that that thread is done with it's work.
procedure TForm2.Button5Click(Sender: TObject);
begin
TThread.CreateAnonymousThread(procedure ()
var i : integer;
begin
inc(i); // some work here
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
label1.Caption := TThread.Current.ThreadID.ToString;
end);
end).Start;
end;
TThread.CreateAnonymousThread is a function which returns an instance of new Thread like this:
var ms:TThread;
ms:=TThread.CreateAnonymousThread( .....
You have an instance - ms "aka Thread" and you can work with this object...
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.
I have developed an application that connects to SQL Server database and reads some data from tables every 1 second.
For this purpose I use TTimer but the delay of the database response affects my application performance.
I know a little about TThread in Delphi, what I want to know now is the difference between using TTimer and TThread? And using TThread instead of TTimer is useful for my application performance in this case?
The main difference between the two can be found in their class definition:
TTimer = class(TComponent)
TThread = class
While the TTimer class extends TComponent and is a component itself, TThread is an abstract class which extends TObject.
TThread exposes static methods like TThread.Sleep and a peculiar protected method called Execute which must be implemented in the derived class in order to perform the desired job.
TThread directly uses the Processes and Threads functions of the guest OS.
... for this purpose I use TTimer but the delay of Database response affect on my application performance
The reason why this happens is because the OnTimer event of the TTimer object is executed in the calling thread: when a TTimer component is put into a form and its OnTimer event is implemented, the code is executed in the main thread.
The TThread approach is more flexible: if for some reason the code must be performed in the main thread, this can be achieved nesting a sinchronized block inside the thread's Execute method.
If you want to execute database requests in a repeated manner after some time interval, you better consider using a TThread in combination with a TEvent object.
An example of class definition using TEvent:
TMyThread = class(TThread)
private
FInterval: Integer;
FWaitEvent: TEvent;
protected
procedure Execute; override;
procedure TerminatedSet; override;
public
constructor Create(Interval: Cardinal; CreateSuspended: Boolean);
destructor Destroy; override;
end;
The implemented class:
constructor TMyThread.Create(Interval: Cardinal; CreateSuspended: Boolean);
begin
inherited Create(CreateSuspended);
FInterval := Interval;
FWaitEvent := TEvent.Create(nil, False, False, '');
end;
destructor TMyThread.Destroy;
begin
FWaitEvent.Free;
inherited;
end;
procedure TMyThread.TerminatedSet;
begin
inherited;
FWaitEvent.SetEvent;
end;
procedure TMyThread.Execute;
begin
inherited;
while not Terminated do begin
//do your stuff
//wait fo some amount of time before continue the execution
if wrSignaled = FWaitEvent.WaitFor(FInterval) then
Break;
end;
end;
The WaitFor method called on the FWaitEvent object allows to wait for the desired amount of time.
The implementation of the thread's TerminatedSet method allows to put the FWaitEvent object in a signaled state and then exit from the thread before the interval has elapsed.
TTimer is a message-based timer. It posts WM_TIMER messages to the message queue of the thread that creates it. Your database operations are blocking that thread from processing new messages in a timely manner. Assuming your TTimer is in the main UI thread, that is why your app performance suffers. Moving the database operations into a worker thread prevents the main thread's message loop from being blocked.
This doesn't specifically address your q, but as noted in a comment to one of
the other answers, polling a database at the frequency you're doing isn't a good idea, especially if other users are trying to access it.
There are various ways to get notifications from database servers when data changes, without needing to continually poll them. This Embarcadero paper has a very useful review of what's available for various DBMSs:
http://docwiki.embarcadero.com/RADStudio/XE8/en/Database_Alerts_%28FireDAC%29
If your Delphi version includes FireDAC, as you'll see from the link that you could use TFDEventAlerter to receive notifications of data changes on the server if your DBMS supports
it.
If you're using Interbase or Firebird (and maybe some others), there are alternate Delphi components available that don't require FireDAC, e.g. TIBEventAlerter in the IBExpress ibrary for Interbase.
I would suggest keeping your TTimer if you want to on your main form
Then inside your TTimer create a TTask
http://docwiki.embarcadero.com/RADStudio/XE8/en/Tutorial:_Using_Tasks_from_the_Parallel_Programming_Library
https://delphiaball.co.uk/2014/09/08/using-ttask-from-the-parallel-programming-library/
And doing all your DB work in there, but as others have suggested checking every 1 second is not very good practice.
Something like this :
Global var downloaddata : ITask
TimerTimer.Timer(Sender: TObject);
begin
if downloaddata.Status = TTaskStatus.Running then
begin
//If it is already running don't start it again
Exit;
end;
downloaddata := TTask.Create (procedure ()
var //Create Thread var here
MyConnection : TFDConnection;
MyQuery : TFDQuery;
begin
//Do all your Query logic in here
//If you need to do any UI related modifications
TThread.Synchronize(TThread.CurrentThread,procedure()
begin
//Remeber to wrap them inside a Syncronize
end);
//If you have Query variables and this is running on mobile with ARC
//remember to set their connection : properties to nil to avoid memory leaks
//http:stackoverflow.com/questions/32010583/what-happens-to-database-connection-objectsmydac-tmyconnection-under-arc
MyQuery.connection := nil
end);
downloaddata.start
There are much better solutions available this is just a quick basic answer but it should guide you into something better.
Doing logic in your thread would keep your UI repsonsive, but beware that TThread.Syncronize will wait for the main form and depending on the situation TThread.queue would be a better call.
Whether TTimer or TThread is used, it is recommended to run a query only to get data that has been changed. To do that you need to:
Add a 'modified' (TIMESTAMP) column to each table
Add a 'deleted' (TIMESTAMP) column to each table
Add a trigger for INSERT OR UPDATE to update 'modified' field with CURRENT_TIMESTAMP
Add a DESC index on the 'modified' field to speed up queries
Never delete a row, only update 'deleted' field with CURRENT_TIMESTAMP
After first read, it is enough to ask for new data:
select c.* from 'customers' as c where c.modified > '2019...'
You read all data at once and store the result in a temporary memory-array.
After closed the dataset >> you compare (syncronized) with the main-array.
To update data >> run a separate SQL.
Right now have a multi-thread scheme like this:
//global variables
var
Form1: TForm1;
ControlFile: TextFile;
MaxThreads, iThreads: integer;
MyCritical: TCriticalSection;
The ControlFile is accessed by the threads, that do a ReadLn, and perform actions with the line obtained:
procedure TForm1.Button2Click(Sender: TObject);
var
HostLine: AnsiString;
FileHandle: integer;
begin
MyCritical:= TCriticalSection.Create;
MaxThreads:= 100;
iThreads:= 0;
while not(eof(ControlFile)) and (iThreads < MaxThreads) do
begin
inc(iThreads);
ReadLn(ControlFile, HostLine);
MyThread.Create(HostLine);
end;
end;
this block is the first doubt. I'm creating 100 threads, each one created received the current line of the textfile. But the problem is that on threads.onterminate, I execute this:
procedure MyThread.MainControl(Sender: TObject);
var
HostLine: string;
begin
try
MyCritical.Acquire;
dec(iThreads);
while not(eof(ControlFile)) and (iThreads < MaxThreads) do
begin
inc(iThreads);
ReadLn(ControlFile, HostLine);
MyThread.Create(HostLine);
end;
finally
MyCritical.Release;
end;
end;
The idea is to keep creating new threads, until the textfile finishes. But if one thread terminate, and execute this procedure, before the first while finished, what happens? The main thread, from button2click will be accessing the file, and the thread's procedure too. This looks strange to me. And the Critical Section, should be global or thread local? And this procedure, MainControl, that opens new threads until the end of the file, should be global or thread local?
First of all, I am not sure it's such a stellar idea to have different threads read from the same text file. It's not that it can't work, but I think it would be much cleaner to simply read the whole thing into a TStringList variable up front, which can then be shared among threads, if needed.
If you do go with what you already have, your critical section must be acquired in the main loop also - the threads that you spawn will start executing immediately by default, so it looks like there could be a race between your main thread and the threads that run MainControl, though you don't show exactly how that call is going to be made.
The critical section needs to be a global variable, as you have it, or a field/property of a global class in order to be shared among threads.
My final point is that it's probably not the greatest idea to create 100 threads either. Unless your threads are mostly waiting on I/O or events, you should generally not have more threads than you have CPU cores. It's better to use a pool of worker threads and a queue of work items that can then be doled out to the running threads. There's supposedly built-in support for some of this in more recent Delphi RTLs. I personally use my own tried and true custom thread pool implementation, so I can't give you any specific help with that part.
The call to OnTerminate is already synchronised.
procedure TThread.DoTerminate;
begin
if Assigned(FOnTerminate) then Synchronize(CallOnTerminate);
end;
So the critical section is not actually needed at all because all the code you've shown runs in the context of the main thread.
I've built a simple logging class and want to confirm that it is thread safe. Basically the Log, RegisterLogger and UnRegisterLogger will be called from different threads. Log will be called alot (from many different threads) and RegisterLogger and UnRegisterLogger infrequently.
Basically my question can be boiled down to is: "Are reads on TList<x> thread safe?", that is to say can I have multiple threads accessing a TList at the same time.
IExecutionCounterLogger is an interface with a Log method (with the same signature as TExecutionCounterServer.Log)
Type
TExecutionCounterServer = class
private
Loggers : TList<IExecutionCounterLogger>;
Synchronizer : TMultiReadExclusiveWriteSynchronizer;
public
procedure RegisterLogger(Logger : IExecutionCounterLogger);
procedure UnRegisterLogger(Logger : IExecutionCounterLogger);
procedure Log(const ClassName, MethodName : string; ExecutionTime_ms : integer);
constructor Create;
destructor Destroy; override;
end;
constructor TExecutionCounterServer.Create;
begin
Loggers := TList<IExecutionCounterLogger>.Create;
Synchronizer := TMultiReadExclusiveWriteSynchronizer.Create;
end;
destructor TExecutionCounterServer.Destroy;
begin
Loggers.Free;
Synchronizer.Free;
inherited;
end;
procedure TExecutionCounterServer.Log(const ClassName, MethodName: string; ExecutionTime_ms: integer);
var
Logger: IExecutionCounterLogger;
begin
Synchronizer.BeginRead;
try
for Logger in Loggers do
Logger.Log(ClassName, MethodName, ExecutionTime_ms);
finally
Synchronizer.EndRead;
end;
end;
procedure TExecutionCounterServer.RegisterLogger(Logger: IExecutionCounterLogger);
begin
Synchronizer.BeginWrite;
try
Loggers.Add(Logger);
finally
Synchronizer.EndWrite;
end;
end;
procedure TExecutionCounterServer.UnRegisterLogger(Logger: IExecutionCounterLogger);
var
i : integer;
begin
Synchronizer.BeginWrite;
try
i := Loggers.IndexOf(Logger);
if i = -1 then
raise Exception.Create('Logger not present');
Loggers.Delete(i);
finally
Synchronizer.EndWrite;
end;
end;
As a bit more background, this is a follow on from this question. Basically I've added some instrumentation to every method of a (DCOM) DataSnap server, also I've hooked into every TDataSnapProvider OnGetData and OnUpdateData event.
Are reads on TList<T> thread safe? That is to say can I have multiple threads accessing a TList<T> at the same time?
That is thread safe and needs no synchronisation. Multiple threads can safely read concurrently. That is equivalent to (and in fact implemented as) reading from an array. It is only if one of your threads modifies the list that synchronisation is needed.
Your code is a little more complex than this scenario. You do appear to need to cater for threads modifying the list. But you've done so with TMultiReadExclusiveWriteSynchronizer which is a perfectly good solution. It allows multiple reads threads to operate concurrently, but any write threads are serialized with respect to all other threads.
Emphasizing the first part of your question, you state that calls to RegisterLogger and UnregisterLogger are infrequently. While the Log call is only reading the list, these other two are changing the list. In this case you have to make sure that none of these is executed while a Log call is executing or may occur.
Imagine a Delete in UnregisterLogger is executed during the for loop in Log. The results are unpredictable at least.
It will be not sufficient to use the Synchronizer only in those two writing calls.
So the answer to your question
Are reads on TList thread safe?
can only be: it depends!
If you can make sure that no RegisterLogger and UnregisterLogger happen (i.e. only read calls can happen), you can safely omit the Synchronizer. Otherwise - better not.