Working with database in the OnExecute event (Indy) - multithreading

i have a server with these codes :
procedure TFrmMain.TCPServerExecute(AContext: TIdContext);
begin
Res := DoRegister(Name,Family,Username,Password);
end;
function TFrmMain.DoRegister(Name,Family,Username,Password:string): bool;
var
Qry: TSQLQuery;
begin
Qry := TSQLQuery.Create(nil);
try
Qry.SQLConnection := FrmConnect.SQLConnection;
Qry.SQL.Text :='INSERT INTO `table` ...';
Qry.ExecSQL();
finally
Qry.Free;
end;
Result := True;
end;
is there any problem with access to one table in various Threads ? and totally what is dangerous to be used in the Onexecute event ?
Thank you for response friends .
so , is it a true way to make different connections for different threads ?
var
Qry: TSQLQuery;
SqlCon: TSQLConnection;
Begin
SqlCon := TSQLConnection.Create(nil);
Qry := TSQLQuery.Create(nil);
try
SqlCon := FrmConnect.SQLConnection;
Qry.SQLConnection := SqlCon;
finally
SqlCon.Free;
Qry.Free;
end;
end;

Your second code fragment is not correct. You're overwriting the new connection with the global connect, when you should be copying out the connection string. You're also freeing that global which will probably cause problems for the rest of your application. Something like this, depending on the details of your TSQLConnection class:
SqlCon := TSQLConnection.Create(nil); // create
Qry := TSQLQuery.Create(nil);
try
//SqlCon := FrmConnect.SQLConnection; // overwrite!!!
SqlCon.ConnectionString := FrmConnect.SQLConnection.ConnectionString;
SqlCon.Active := true;
Qry.SQLConnection := SqlCon;
...
If you want to have a databse connection pool it's quite tricky because the connections are usually thread-specific - you need one per thread and you can't pass them between threads. So you end up writing a lot of code to support that.
I now use the OmniThreadLibrary and have a factory method that returns a new database connection. That gives me a thread pool that I feed tasks into, so my specific task is bound to an existing thread when it executes, but the thread is fairly long-lived. The code I had to write to get this is very small (I'm using ADO):
type
// a factory to generate new instances of our thread-specific data
IThreadPoolData = interface
['{14917B01-6613-4737-B87E-0046789D4284}']
function GetConnection: TADOConnection;
function GetStoredProc: TADOStoredProc;
end;
TThreadPoolData = class(TInterfacedObject, IThreadPoolData)
strict private
FADOConnection: TADOConnection;
FStoredProc: TADOStoredProc; // lazy creation!
public
constructor Create(aConnectionString: string); overload;
destructor Destroy; override;
function GetConnection: TADOConnection;
function GetStoredProc: TADOStoredProc;
end;
// create the connection here so thread creation is slow but using it
// is (relatively) fast
constructor TThreadPoolData.Create(aConnectionString: string);
begin
FADOConnection := TADOConnection.Create(nil);
FADOConnection.LoginPrompt := false;
FADOConnection.ConnectionString := aConnectionString;
FADOConnection.ConnectOptions := coAsyncConnect;
FADOConnection.Connected := true;
end;
destructor TThreadPoolData.Destroy;
begin
FADOConnection.Connected := false;
if assigned(FStoredProc) then
FreeAndNil(FStoredProc);
FreeAndNil(FADOConnection);
end;
You will need to do something similar if you write your own thread or connection pool.

Each thread accessing DB should have its own connection, you cannot share a DB connection between several threads. OnExecute event is invoked in the context of the thread corresponding to the requesting client, so each time it is invoked, it is executed inside a worker thread, and such a thread should have its own DB connection.
If you do not want to establish a new connection for each worker thread; one option could be, you dedicate a single thread for DB connection, and delegate all DB operations to that thread, for example your other threads can send their INSERT SQL statements to a queue in that DB thread, and that DB thread executes them one-by-one using a single DB connection. Of course if you take this approach, all DB load would be on a single thread, and if you have so many DB operations, then that DB thread itself could be a performance bottleneck! What's more, taking this approach, query executions would be asynchronous except you use a synchronization technique whenever each of your threads ask the DB thread to execute a DB query for them.
Also take note that if your DB access components are ADO, then you have to call CoInitialize and CoUninitialize, because Delphi runtime only does that for the main thread not other threads which are created by you.

I would use a connection pool for the database connections. Every thread then only requests a connection from the pool when needed (which might block if there are currently no free connections in the pool) and then uses and finally returns it to the pool. A pool has the advantage that there are less connections needed than there are concurrent threads, and the connections are already present when needed.

Yes and no. You can access a single table from different threads, but you need a TSQLConnection instance per thread to do so safely.
Update
Instantiating a different connection for each thread is fine. It's what most webpages do all the time as well (server side scripting using asp, php or ... means state-less execution and thus connections usually do not survive to the next request and have to be re-established).
If you are worried about the overhead, you can consider using a single connection like vcldeveloper suggests. You will have to ensure that any variables and member fields used by that "connection thread" which are changed by the other threads (for example the field member receiving the SQL to be executed), will have to be protected by some sort of synchronisation mechanism.
The same applies to the connection pool as suggested by mjustin, though in that case, the connection pool needs to be protected by synchronisation mechanisms.

Related

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.

What is the difference between using TTimer and using TThread

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.

infinite loops in separate threads

I have some data base optimization routines that need to execute periodically. I am currently using a TTimer but the main VCL freezes and is very hacky ... I would like to know what the best method is to have a low cpu consumption and execute these routines. I think putting the routines in separate threads with low thread priority is the best way.
Any ideas ?
If possible, it is much better to just code all your threads to do the most important thing that needs to get done at that particular time. Messing with thread priorities can cause serious performance problems if you don't know exactly what you're doing. Instead, just code your threads like this:
Is there anything important to do? If so do it.
Is there anything not too important to do? If so, do a little of it.
Go to step 1.
Say you do use thread priorities. Imagine this:
A low priority task, A, grabs a lock on the database.
A normal priority task, B, requires lots of CPU time, it steals the CPU from the low priority task.
A normal priority task, C, requires access to the database. But it can't run because the low priority task holds the lock on the database and task B gets the CPU over task A.
Now, task C has to wait until task B is complete to get access to the database. But it should be timeslicing with task B.
One way of doing it is creating your "db optimization thread" something like:
type
// a record defining database connection
TConnectionSettings = record
DatabaseName: string;
Server: string;
Port: Word;
UserName: string;
Password: string;
end;
type
TDBOptimizationThread = class(TThread)
private
FConnection: TDatabaseConnection; // your database connection... I don't know what libraries you are using
FQuery: TQuery; // your specific db query
protected
procedure Execute; override;
public
constructor Create(AConnectionSettings: TConnectionSettings;
destructor Destroy; override;
end;
implementation
constructor TDBOptimizationThread.Create(AConnectionSettings: TConnectionSettings;
begin
inherited Create(True); // create suspended
//FreeOnTerminate := True; // if you want it to be freed when you terminate it
// create FConnection and FQuery objects
// setup FConnection parameters based on AConnectionSettings
end;
destructor TDBOptimizationThread.Destroy;
begin
// destroy objects
inherited Destroy;
end;
procedure TDBOptimizationThread.Execute;
begin
while NOT Terminated do
try
// check if it's time to run query
// you can use a private variable of TDateTime type that will hold
// last timestamp of when the query ran, etc.
if ItsTimeToRunQuery then begin
// check if we still have db connectivity
if NOT FConnection.Connected then
// ouch, try to connect...
FConnection.Connect;
FQuery.SQL.Text := 'Your optimization query';
FQuery.Execute; // or ExecSQL or whatever the method is based on your db library
end;
except
on E: Exception do begin
// log exception, something went wrong!!
end;
end;
end;
It is very important that your db connection is created and destroyed in this thread, otherwise you will have issues...
So, let's start a db optimization thread
...
var
LConnSettings: TConnectionSettings;
// you may want a private TDBOptimizationThread variable rather than
// a variable in a method, but I leave that to you
LDBOptimizationThread: TDBOptimizationThread;
begin
LConnSettings.Database := 'MyDatabase';
LConnSettings.Port := 1234;
LConnSettings.Server := 'localhost';
// continue with connection settings...
LDBOptimizationThread := TDBOptimizationThread.Create(LConnSettings);
LDBOptimizationThread.Start; // start it
end;
You can of course make it a low priority, but if your queries are not going to run for more than a few seconds at each time, I don't see a reason for that, but feel free to contradict.
IMHO, a low priority thread is the way to go for this kind of task. But you do not have to create different threads for each optimization routine, handle all of them with only one thread. So it will be easier for you to execute them in some specific order or with different frequencies and you will be sure that they do not get into way of each other (from the point of DB).

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!.

Best approach for thread synchronized queue

I have a queue in which I can enqueue different threads, so I can assure two things:
Request are processed one by one.
Request are processed in the arriving order
Second point is important. Otherwise a simple critical section would be enough.
I have different groups of requests and only inside a single group these points must be fulfilled. Requests from different groups can run concurrent.
It looks like this:
FTaskQueue.Enqueu('MyGroup');
try
Do Something (running in context of some thread)
finally
FTaskQueue.Dequeu('MyGroup');
end;
EDIT: I have removed the actual implementation because it hides the problem I want to solve
I need this because I have an Indy based web server that accepts http requests. First I find a coresponding session for the request. Then the request (code) is executed for that session. I can get multiple requests for the same session (read I can get new requests while the first is still processing) and they must execute one by one in correct order of arrival. So I seek a generic synchronization queue that can be use in such situations so requests can be queued. I have no control over the threads and each request may be executed in a different thread.
What is best (ususal) approach to this sort of problem? The problem is that Enqueue and Dequeue must be atomic opeations so that correct order is preserverd. My current implementation has a substantial bottleneck, but it works.
EDIT: Bellow is the problem of atomic Enqueue / Dequeue operations
You wold normaly do something like this:
procedure Enqueue;
begin
EnterCriticalSection(FCritSec);
try
DoEnqueue;
finally
LeaveCriticalSection(FCritSec);
end;
BlockTheCurrentThread; // here the thread blocks itself
end;
procedure Dequeue;
begin
EnterCriticalSection(FCritSec);
try
DoDequeue;
UnblockTheNextThread; // here the thread unblocks another thread
finally
LeaveCriticalSection(FCritSec);
end;
end;
Now the problem here is that this is not atomic. If you have one thread already in the queue and another one comes and calls Enqueue, it can happen, that the second thread will just leave the critical section and try to block itself. Now the thread scheduler will resume the first thread, which will try to unblock the next (second) thread. But second thread is not blocked yet, so nothing happens. Now the second thread continues and blocks itself, but that is not correct because it will not be unblocked. If blocking is inside critical section, that the critical section is never leaved and we have a deadlock.
Another approach:
Let each request thread have a manual reset event that is initially unset. The queue manager is a simple object which maintains a thread-safe list of such events. The Enqueue() and Dequeue() methods both take the event of the request thread as a parameter.
type
TRequestManager = class(TObject)
strict private
fCritSect: TCriticalSection;
fEvents: TList<TEvent>;
public
constructor Create;
destructor Destroy; override;
procedure Enqueue(ARequestEvent: TEvent);
procedure Dequeue(ARequestEvent: TEvent);
end;
{ TRequestManager }
constructor TRequestManager.Create;
begin
inherited Create;
fCritSect := TCriticalSection.Create;
fEvents := TList<TEvent>.Create;
end;
destructor TRequestManager.Destroy;
begin
Assert((fEvents = nil) or (fEvents.Count = 0));
FreeAndNil(fEvents);
FreeAndNil(fCritSect);
inherited;
end;
procedure TRequestManager.Dequeue(ARequestEvent: TEvent);
begin
fCritSect.Enter;
try
Assert(fEvents.Count > 0);
Assert(fEvents[0] = ARequestEvent);
fEvents.Delete(0);
if fEvents.Count > 0 then
fEvents[0].SetEvent;
finally
fCritSect.Release;
end;
end;
procedure TRequestManager.Enqueue(ARequestEvent: TEvent);
begin
fCritSect.Enter;
try
Assert(ARequestEvent <> nil);
if fEvents.Count = 0 then
ARequestEvent.SetEvent
else
ARequestEvent.ResetEvent;
fEvents.Add(ARequestEvent);
finally
fCritSect.Release;
end;
end;
Each request thread calls Enqueue() on the queue manager and afterwards waits for its own event to become signalled. Then it processes the request and calls Dequeue():
{ TRequestThread }
type
TRequestThread = class(TThread)
strict private
fEvent: TEvent;
fManager: TRequestManager;
protected
procedure Execute; override;
public
constructor Create(AManager: TRequestManager);
end;
constructor TRequestThread.Create(AManager: TRequestManager);
begin
Assert(AManager <> nil);
inherited Create(TRUE);
fEvent := TEvent.Create(nil, TRUE, FALSE, '');
fManager := AManager;
Resume;
end;
procedure TRequestThread.Execute;
begin
fManager.Enqueue(fEvent);
try
fEvent.WaitFor(INFINITE);
OutputDebugString('Processing request');
Sleep(1000);
OutputDebugString('Request processed');
finally
fManager.Dequeue(fEvent);
end;
end;
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
begin
for i := 1 to 10 do
TRequestThread.Create(fRequestManager);
end;
The queue manager locks the list of events both in Enqueue() and in Dequeue(). If the list is empty in Enqueue() it sets the event in the parameter, otherwise it resets the event. Then it appends the event to the list. Thus the first thread can continue with the request, all others will block. In Dequeue() the event is removed from the top of the list, and the next event is set (if there is any).
That way the last request thread will cause the next request thread to unblock, completely without suspending or resuming threads. This solution does also not need any additional threads or windows, a single event object per request thread is all that is needed.
I'll answer with the additional information from your comment taken into consideration.
If you have a number of threads that need to be serialized then you could make use of the serialization mechanism Windows provides for free. Let each queue be a thread with its own window and a standard message loop. Use SendMessage() instead of PostThreadMessage(), and Windows will take care of blocking the sending threads until the message has been processed, and of making sure that the correct execution order is maintained. By using a thread with its own window for each request group you make sure that multiple groups are still processed concurrently.
This is a simple solution that will work only if the request itself can be handled in a different thread context than it originated in, which shouldn't be a problem in many cases.
Did you try the TThreadList object provided by Delphi ?
It is thread safe and it manage the locks for you. You manage the list "outside" the thread, within your main thread.
As requests ask for a new task, you add it to the list. When a thread finishes, with the OnTerminate event you can call the next thread in the list.

Resources