Restart Delphi TThread that lives the entire app lifetime - multithreading

I have created a class that derives from TThread, because I wish to do some async stuff, however to avoid having to create another class, I built the entire thing around that thread class. Not sure if this is good practice or not, and if I cant get this to work, well then I suppose I have no choice but to recode..
The problem: I create the Thread on FormCreate, assign some properties, and I Free it on FormDestroy. In the Thread's constructor, I set FreeOnTerminate = False. When I click on a button on my Form, I Start(); the Thread. Okay, so it runs as expected, an error occurs (expected!), its being passed to my error handling event, and it appears to terminate. I then click the button again, and I get a Cannot call Start on a running or suspended thread error.
How can I finish the thread without freeing it, and enabling me to start it again?

You can't restart a thread once it is finished/terminated. In that case you should just create a new instance if the thread again like you did in FormCreate.

Catch the error in the thread, handle it there and then let the thread continue the work. To handle the error you could simply queue a method to the main thread to report the error, for example. I hope you aren't letting exceptions leave your thread Execute method.

This is the way that I implement it:
procedure TAPIRequest.DoRequest;
begin
FBusy := True;
Resume;
end;
procedure TAPIRequest.Execute;
begin
inherited;
while not Terminated do begin
HttpError := False;
try
Response := HTTP.Post(URL, Params);
ParseResponse;
except
HttpError := True;
end;
if Assigned(OnResponse) then
OnResponse();
FBusy := False;
Suspend;
end;
end;

type
TMyThread = class(TThread)
protected
procedure Execute; override;
end;
var
Form1: TForm1;
MyThread: TMyThread;
Event: TEvent;
procedure TForm1.FormCreate(Sender: TObject);
begin
Event := TEvent.Create(nil,true,false, '');
MyThread := TMyThread.Create(False);
end;
procedure TMyThread.Execute;
begin
while True do
begin
Event.WaitFor(Infinite);
// do something
Event.ResetEvent;
end;
end;
procedure RestartThread;
begin
Event.SetEvent;
// if you need check thread status, wait or run, use here
// if Event.WaitFor(0) = ...(wrTimeout, wrSignaled)
end;

Related

Delphi: Verify DataSnap connection via TThread

We have an application in which the user can talk to us, it works fine, he create a new conversation, we chat, and that's ok. But, before start chatting, he needs to connect to the DataSnap Server, and that's where I'm trying to make a Thread. Every 5min, a timer would trigger his event to create the Thread and try to connect on the server, as below:
My Thread:
unit UThreadSnapConnection;
interface
uses
System.Classes, System.SysUtils, Data.SqlExpr;
type
TThreadSnapConnection = class(TThread)
private
FSnap: TSQLConnection;
procedure TryToConnect;
protected
procedure Execute; override;
constructor Create;
public
DMSnap: TSQLConnection;
HostName: String;
Port: String;
end;
implementation
{ TThreadSnapConnection }
constructor TThreadSnapConnection.Create;
begin
inherited Create(True);
FreeOnTerminate := True;
end;
procedure TThreadSnapConnection.TryToConnect;
begin
try
FSnap := DMSnap.CloneConnection;
FSnap.Connected := False;
try
FSnap.Connected := True;
except
end;
if FSnap.Connected then
DMSnap.Connected := True;
finally
FreeAndNil(FSnap);
end;
end;
procedure TThreadSnapConnection.Execute;
begin
Synchronize(TryToConnect);
end;
end.
My Timer:
procedure TMyDataModuleSnap.TimerSnapTimer(Sender: TObject);
var
MyThread: TThreadSnapConnection;
begin
if not(MySQLConnection.Connected) then
begin
MyThread := TThreadSnapConnection.Create;
MyThread.DMSnap := MySQLConnection;
MyThread.HostName := 'localhost';
MyThread.Port := '211';
MyThread.Resume;
end;
end;
What I'm doing is an attempt to connect to the server, if it works, then it will make my data module connect.
My problem is, everytime the line
FSnap.Connected := True;
execute it freezes for 1~2 seconds the application, and the reason I made a thread was to not freeze. As long as I know, it should not bother at all the application, so I started to think maybe it's the work it does when setting the Connected property to True, which will freeze independent if it's thread or not.
Is there any way to not freeze when trying to connect?
And this is my first thread and maybe I just misunderstood things and that's not how thread works, but well, if it is not then I need to know, or at least understand what I'm doing wrong with it.
EDIT: The test I'm doing is, I start the application without starting the server, so it will try to connect unsuccessful, and my data module will not connect too.
There are two options:
while the OnTimer event of a TTimer is executed in the thread which has created the timer, you may consider to create the instance outside the main thread
you may consider to use a TThread class instance
The following applies to the #2.
Using a TEvent in the Execute procedure of your thread you can wait for an amount of FInterval time before the execution of the next block of code.
When the Terminated property is set to True, this approach allows the Execute method to immediately return also during the interval count unlike the adoption of a TThread.Sleep(FInterval); call which would freeze the thread itself for the amount of time specified.
The main thread can be optionally notified using a TNotifyEvent when done.
TMyThread = class(TThread)
private
FInterval: Integer;
FTerminateEvent: TEvent;
protected
procedure Execute; override;
procedure TerminatedSet; override;
public
OnEndJob: TNotifyEvent;
constructor Create(Interval: Cardinal; CreateSuspended: Boolean);
destructor Destroy; override;
end;
constructor TMyThread.Create(Interval: Cardinal; CreateSuspended: Boolean);
begin
inherited Create(CreateSuspended);
FInterval := Interval;
FTerminateEvent := TEvent.Create(nil, False, False, '');
end;
destructor TMyThread.Destroy;
begin
FTerminateEvent.Free;
inherited;
end;
procedure TMyThread.TerminatedSet;
begin
inherited;
FTerminateEvent.SetEvent;
end
procedure TMyThread.Execute;
begin
while not Terminated do begin
//do your stuff
//notify your connection to the main thread if you want
if Assigned(OnEndJob) then
Synchronize(procedure
begin
OnEndJob(Self);
end);
//wait fo some amount of time before continue the execution
if wrSignaled = FterminateEvent.WaitFor(FInterval) then
Break;
end;
end;
Don't synchonize the code you want to be executed in a thread: in Delphi a syncronized block is always executed in the calling thread.
I would have preferred to post a comment rather than an answer, but lack the reputation points; something worth considering when reading the following.
Reading between the lines, it looks like you have a connection to a local SQL server. Access is infrequent causing the connection to drop, so you've instituted a timer to check every 5 mins and re-establish the connection if necessary.
This worked, but you found that the connection attempt blocks program execution until it is established, and so you want to move this operation to a worker thread.
As stated by fantaghirocco, Synchronize causes code to run within the main program thread. My understanding is this code runs after all messages in the main thread have been processed, so you could achieve the same result by having the timer post a message, and the associated message handler call TryToConnect (TryToConnect declared in the main form in this case).
Synchronize is the easiest means of allowing threads to interact with the main thread without having to worry about two or more threads accessing the same object at the same time.
To prevent the connection process from blocking the main program thread, the MySQLConnection Connected property would have to be set in the Execute method of the TThread descendant (not encapsulated within a call to Synchronize).
But doing so introduces the risk of the worker thread and main program accessing MySQLConnection at the same time. To protect against this you would need to introduce a critical section, or similar. If unfamiliar, then check TCriticalSection in the RAD Studio help; there's a section on Critical Sections and an example.
Both the main program and thread would then encapsulate any calls to MySQLConnection within a critical section try finally block:
FLock.Acquire;
try
{code accessing MySQLConnection goes here}
finally
FLock.Release;
end;
Where FLock is a TCriticalSection object.
Any thread attempting to acquire FLock while already acquired by another, will be blocked until FLock is released. This means the main thread would only be blocked if the user attempted access to MySQLConnection when the worker thread was already attempting a connection.
Update:
To get you started, the following is a simple program consisting of two units; Unit1 contains the main form (what you're presented with when you create a new application). The second unit, Unit2 contains a thread. I've done it this way since your thread appears to be in a separate unit.
I've added a button and a critical section to TForm1 (add System.SyncObjs to the uses clause). In the click event of Button1 I create an instance of TMyThread (in your code this would be handled by the timer event):
type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
FLock: TCriticalSection;
end;
var
Form1: TForm1;
implementation
uses Unit2;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
TMyThread.Create;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FLock := TCriticalSection.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FLock.Free;
end;
Unit2 contains the thread. The execute method is a fire once and finish effort. Unit1 is added to the uses clause in the implementation to give the code access to the Form1 variable:
type
TMyThread = class (TThread)
protected
procedure Execute; override;
public
constructor Create;
end;
implementation
uses Unit1;
{ TMyThread }
constructor TMyThread.Create;
begin
inherited Create (False);
end;
procedure TMyThread.Execute;
begin
with Form1 do begin
FLock.Acquire;
try
{access MySQLConnection methods here}
finally
FLock.Release;
end;
end;
end;
When you run this simple program and click Button1, a separate thread is created and the execute method run, following which the thread is destroyed. This process is repeated every time you click Button1.
If you place a breakpoint in Unit1 on the MyThread := TMyThread.Create line, and another breakpoint in Unit2 on the FLock.Acquire line, run the program and click Button1, the code will stop in the main thread; the thread Id shown in the left hand pane.
If you click F9 to continue program execution, it will then stop on the Unit2 breakpoint. You'll note the thread Id is now different, and the Thread Status window down the bottom of the IDE now lists this extra thread. When you press F9 again and this new thread disappears.
This program does nothing, but you would place whatever MySQLConnection code you needed to run in this thread where I have the comment in the Try Finally block.
In the main thread, wherever MySQLConnection's methods are accessed, you will also need to encapsulate these within a FLock try finally block. For example, if you have a TClientDataSet connected to a TDataSetProvider connected to a TSQLDataSet connected to your MySQLConnection, then opening the TClientDataSet will have to be encapsulated within this FLock Try Finally:
begin
FLock.Acquire;
try
CDS.Open;
finally
FLock.Release;
end;
end;
Where CDS is the TClientDataSet.
The code you intend to run in the thread basically closes the connection and re-opens it. A side benefit of the critical section (if properly configured, and all access to MySQLConnection protected by the critical section), is it will prevent the connection being shut in the middle of a user's query.

Delphi 5 + BDE + TStoredProc + Oacle + Thread

i'm trying to develop a application that runs a stored procedure in a background mode.
1 - If i run in the mains thread, it's OK, it's lock the screen.
2 - If i put the stpGen.ExecProc; in a thread, it's runs OK, but it's lock the mains thread too.
is there a option to run TStoredProc in a thread and don't lock the main thread?
Code:
type
TStoredProcSegundoPlano = class(TThread)
private
stpGen: TStoredProc;
excErro: Exception;
protected
procedure Execute; override;
public
constructor Create(const stpGen: TStoredProc; const blnAutoFree, blnAutoStart: Boolean);
property Erro: Exception read excErro;
end;
implementation
{ TStoredProcSegundoPlano }
constructor TStoredProcSegundoPlano.Create(const stpGen: TStoredProc;
const blnAutoFree, blnAutoStart: Boolean);
begin
inherited Create(True);
Self.excErro := nil;
Self.stpGen := stpGen;
if blnAutoFree then
FreeOnTerminate := True;
if blnAutoStart then
Resume;
end;
procedure TStoredProcSegundoPlano.Execute;
begin
try
try
stpGen.ExecProc;
except
on E: Exception do
begin
excErro := AcquireExceptionObject;
end;
end;
finally
Terminate;
end;
end;
so, i call this way:
TStoredProcSegundoPlano.Create(stpGen, True, True);
in this moment, the application is ok, but few seconds after, the maisn threads lock, i don't have any idea know to resolve this, it's possivel to run a TStoredProc un a background?
Thanks

How do I stop a thread before it's finished running?

I have a thread called TMyThread and I overrode the Execute procedure like this:
procedure TMyThread.Execute;
Begin
repeat
//Some Work
Sleep(5000);
//Some Work 2;
Sleep(5000);
until FActive=False
End;
In the main form I have button called 'Destroy My Thread'. I want to destroy my thread but the problem is that my thread will be destroyed only if it finish its work. I want to destroy my thread even it has not finished its work. How do I do this?
Your thread's Execute method must regularly check the state of the thread's Terminated property. And if it is True, then the thread Execute method must exit.
So, a typical Execute method might look like this:
procedure TMyThread.Execute;
begin
while not Terminated do
DoNextPieceOfWork
end;
It looks like your thread has its own FActive flag that is performing the same task. The problem with that is that TThread doesn't know about it. So you should get rid of FActive and instead use the built in mechanism.
When you call Free on the thread it will call Terminate. That sets Terminated to be True. Then it waits for the thread method to exit. That will happen because your thread notices that Terminated is True and quits. And then the thread's destructor can continue and finish the job of tidying up the thread.
Looking at the code in your answer, it would be better written to make use of the existing Terminate mechanism.
type
TMyThread = class(TThread)
private
FTerminateEvent: TEvent;
protected
procedure Execute; override;
procedure TerminatedSet; override;
public
constructor Create(ACreateSuspended: Boolean);
destructor Destroy; override;
end;
constructor TMyThread.Create(ACreateSuspended: Boolean);
begin
inherited Create(ACreateSuspended);
FTerminateEvent := TEvent.Create(nil, True, False, '');
end;
destructor TMyThread.Destroy;
begin
inherited;
FTerminateEvent.Free;
end;
procedure TMyThread.TerminatedSet;
begin
FTerminateEvent.SetEvent;
end;
procedure TMyThread.Execute;
begin
while not Terminated do
begin
// do somthing interesting!
FTerminateEvent.WaitFor(5000);
end;
end;
Now there's no need for a separate Stop method. You can just call Free on the thread. Then Terminate is called. And then TerminatedSet is called. Then the event is signaled. Then Execute terminates. And then the thread can go away.
Having said that, I'm struggling to think of a scenario where a 5000ms timeout would be the best approach. I don't know why you are doing this, but I'd guess that you are trying to throttle the thread so that it doesn't run hot. You want to avoid a busy loop. That's admirable, but using a fixed timeout is not the way to do it. The way to do it is to wait on a synchronisation event, typically an event. Then when there is more work to be done, the event becomes signaled and your thread wakes up.
Using TEvent can solve this problem
this is an eg :
uses SyncObjs;
TMyThread = class(TThread)
private
FTerminateEvent: TEvent;
protected
procedure Execute; override ;
public
constructor Create(ACreateSuspended: Boolean); overload;
destructor Destroy; override;
procedure Stop;
end;
constructor TMyThread.Create(ACreateSuspended: Boolean);
begin
FTerminateEvent := TEvent.Create(nil, True, False, 'FTerminateEvent');
inherited Create(ACreateSuspended);
end;
destructor TMyThread.Destroy;
begin
FTerminateEvent.Free;
inherited;
end;
procedure TMyThread.Stop;
begin
Terminate;
FTerminateEvent.SetEvent;
end;
procedure TMyThread.Execute;
begin
while not Terminated do
begin
// do somthing interesting!
FTerminateEvent.WaitFor(5000);
end;
end;
Now if i want to kill my thread ,all what i have to do is calling MyThread.Stop than calling MyThread.Free .
You really are approaching this problem completely wrong. Code your threads to only do work that you want them to do, and then there will be no need to "reach in from the outside" to control them. You feel the need to control this thread from the outside because it's doing something you don't want it to do -- well then why did you code it to do that in the first place?

Delphi 7: How to implement multi-threading?

I have a TButton in the main TForm. When user click the button, it will execute the below process:
begin
Process_done := FALSE;
Process_Result.Clear;
cmdProcess.CommandLine := #34+AppPath+'getdata.exe"';
cmdProcess.Run;
Repeat
Application.ProcessMessages;
Until Process_done;
end;
As you can see above, the process calls external executable, and the process can take some times which blocking the main application.
This is only one process, and I need another one.
So, I am thinking to implement multi-threading, where I can run the above process in a separate thread. The other process as well. And the main thread can do something WHILE checking when both processes done.
Can anyone give me some examples how to do this using Delphi 7?
OR point me to an article, simple implementation like this?
Thanks.
Try something like this:
type
TRunProcessThread = class(TThread)
protected
cmdProcess: Whatever;
procedure Execute; override;
public
constructor Create(const ACmdLine: String);
destructor Destroy; override;
end;
constructor TRunProcessThread.Create(const ACmdLine: String);
begin
inherited Create(True);
FreeOnTerminate := True;
cmdProcess := Whatever.Create;
cmdProcess.CommandLine := ACmdLine;
end;
destructor TRunProcessThread.Destroy;
begin
cmdProcess.Free;
inherited;
end;
procedure TRunProcessThread.Execute;
begin
cmdProcess.Run;
...
end;
.
procedure TForm1.Button1Click(Sender: TObject);
var
Thread: TRunProcessThread;
begin
Thread := TRunProcessThread.Create(AnsiQuotedStr(AppPath + 'getdata.exe', #34));
Thread.OnTerminate := ProcessDone;
Thread.Resume;
end;
procedure TForm1.ProcessDone(Sender: TObject);
begin
// access TRunProcessThread(Sender) to get result information as needed ...
end;
You should create a class inherited from TThread and put that code in there. I don't remember exactly, but I think you'll find TThread template in File->New dialog box. When code execution is finished, you just notify your gui. Here's an article how to synchronize UI with external thread http://delphi.about.com/od/kbthread/a/thread-gui.htm

How can a thread notify an object that doesn't have a window handle?

I'm new to multithreading, but not a complete novice. I need to perform a call to a webservice in a worker thread.
In the main thread I have a form (TForm) with a private data member (private string) that only the worker thread will write to (I pass the a pointer to it into the thread before it resumes). When the worker thread has finished its webservice call and written the resultant response xml to the private member on the form, the worker thread uses PostMessage to send a message to the form's handle (which I also passed into the thread before it resumed).
interface
const WM_WEBSERVCALL_COMPLETE = WM_USER + 1;
type
TWebServiceResponseXML = string;
PWebServiceResponseXML = ^TWebServiceResponseXML;
TMyForm = class(TForm)
...
private
...
fWorkerThreadID: Cardinal;
fWebServiceResponseXML: TWebServiceResponseXML;
public
...
procedure StartWorkerThread;
procedure OnWebServiceCallComplete(var Message: TMessage); Message WM_WEBSERVCALL_COMPLETE;
end;
TMyThread = class(TThread)
private
protected
procedure Execute; override;
public
SenderHandle: HWnd;
RequestXML: string;
ResponseXML: string;
IMyService: IService;
PResponseXML: PWebServiceResponseXML;
end;
implementation
procedure TMyForm.StartWorkerThread;
var
MyWorkerThread: TMyThread;
begin
MyWorkerThread := TMyThread.Create(True);
MyWorkerThread.FreeOnTerminate := True;
MyWorkerThread.SenderHandle := self.Handle;
MyWorkerThread.RequestXML := ComposeRequestXML;
MyWorkerThread.PResponseXML := ^fWebServiceResponseXML;
MyWorkerThread.Resume;
end;
procedure TMyForm.OnWebServiceCallComplete(var Message: TMessage);
begin
// Do what you want with the response xml string in fWebServiceResponseXML
end;
procedure TMyThread.Execute;
begin
inherited;
CoInitialize(nil);
try
IMyService := IService.GetMyService(URI);
ResponseXML := IMyService.Search(RequestXML);
PResponseXML := ResponseXML;
PostMessage(SenderHandle, WM_WEBSERVCALL_COMPLETE, 0, 0);
finally
CoUninitialize;
end;
end;
It works great, but now I want to do the same thing from a datamodule (which doesn't have a Handle)... so I would really appreciate some useful code to supplement the working model I have.
EDIT
What I really want is the code (if possible) that would allow me to replace the line
MyWorkerThread.SenderHandle := self.Handle;
with
MyWorkerThread.SenderHandle := GetHandleForThisSOAPDataModule;
I have used this technique before with some success: Sending messages to non-windowed applications
Basically, use a second thread as a message pump on a handle obtained via AllocateHWND. This is admittedly irritating, and you would be better off using a library to handle all the details. I prefer OmniThreadLibrary but there are others - see How Do I Choose Between the Various Ways to do Threading in Delphi? and Delphi - Threading frameworks.
You can allocate you own handle with AllocateHwnd and use that as a PostMessage target.
TTestThread = class(TThread)
private
FSignalShutdown: boolean;
// hidden window handle
FWinHandle: HWND;
protected
procedure Execute; override;
// our window procedure
procedure WndProc(var msg: TMessage);
public
constructor Create;
destructor Destroy; override;
procedure PrintMsg;
end;
constructor TTestThread.Create;
begin
FSignalShutdown := False;
// create the hidden window, store it's
// handle and change the default window
// procedure provided by Windows with our
// window procedure
FWinHandle := AllocateHWND(WndProc);
inherited Create(False);
end;
destructor TTestThread.Destroy;
begin
// destroy the hidden window and free up memory
DeallocateHWnd(FWinHandle);
inherited;
end;
procedure TTestThread.WndProc(var msg: TMessage);
begin
if Msg.Msg = WM_SHUTDOWN_THREADS then
// if the message id is WM_SHUTDOWN_THREADS
// do our own processing
FSignalShutdown := True
else
// for all other messages call
// the default window procedure
Msg.Result := DefWindowProc(FWinHandle, Msg.Msg,
Msg.wParam, Msg.lParam);
end;
You can apply this to anything not just threads. Just beware that AllocateHWND is NOT threade safe as indicated here.
Alternatives based on the use of an event:
Use OnTerminate of the thread (already present) in combination with a flag:
TMyDataModule = class(TDataModule)
private
procedure OnWebServiceCallComplete(Sender: TObject);
...
TMyThread = class(TThread)
public
property TerminateFlag: Integer ...
...
procedure TMyDataModule.StartWorkerThread;
...
MyWorkerThread.OnTerminate := <Self.>OnWebServiceCallComplete;
...
procedure TMyDataModule.OnWebServiceCallComplete(Sender: TObject);
begin
if MyWorkerThread.TerminateFlag = WEBCALL_COMPLETE then
...
end;
Set the TerminateFlag in the Execute routine. OnTerminate will automatically fire, even if FreeOnTerminate is True.
Add a new event property to the thread class in which you may provide the flag as a parameter to indicate termination/thread result. Something like shown here. Be sure to synchronize the event call. Or forget the parameter and just only call the event if execution completed gracefully (like you're doing now).

Resources