Our program creates a background thread at the beginning of the program. The background thread does some database integrity checks and checks for stuff in the Internet using Indy. After 10 seconds, the background thread should be finished and since FreeOnTerminate is true, it will also clean itself up.
We have noticed that in some cases, if the user closes the program too quickly, the process will still be alive until the background thread is finished.
Since we couldn't exactly reproduce the issue, I have created a demo project to try a few things:
type
TBackgroundThread = class(TThread)
protected
procedure Execute; override;
end;
{ TForm1 }
var
bt: TBackgroundThread;
procedure TForm1.FormCreate(Sender: TObject);
var
i: integer;
begin
// Create a background thread which runs X seconds and then terminates itself.
bt := TBackgroundThread.Create(false);
bt.FreeOnTerminate := true;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
// The user closes the app while the background thread is still active
Sleep(2000);
Close;
end;
{ TBackgroundThread }
procedure TBackgroundThread.Execute;
var
i: integer;
x: cardinal;
begin
inherited;
// Simulate some work that the background thread does
x := MaxInt;
for i := 0 to MaxInt do
begin
x := Random(x);
end;
end;
The result is a bit surprising to me: After I close the MainForm, the process will be immediately terminated and the background thread will get hard-killed.
Now I have a few questions about this:
After the closing of the MainForm (= exit of the main thread), should I manually terminate all created threads via .Terminate or will that be done automatically?
Shall my threads only check for Self.Terminated or should they also check for Application.Terminated ?
Why does my busy thread as shown above gets immediately killed when I close the application? I expected that the process Project1.exe will run until all threads have finished by themselfes. (And as described above, we had seen an application where the main form is closed, but a thread is preventing the process of being closed).
How is it possible then, that our real application's process does not terminate because of a running background thread? Might it have something to do with the Internet stuff, which might cause the app to wait until a connection timeout is reached?
Closing the main form is not synonymous with exiting the main thread. Code continues to run after the form is closed. In particular, units are finalized.
If you handle your test thread's OnTerminate event, or put a breakpoint in the Terminate method, you'll see that it's not called automatically when your program exits. You'll have to call it yourself. But note also that a thread doesn't stop running just because Terminate is called. It continues running until it stops itself or it's terminated forcefully. Call WaitFor to wait for it to terminate.
Don't bother checking Application.Terminated; the thread's property should be sufficient.
Your thread gets terminated forcefully as your program exits because eventually your program calls ExitProcess, and one of the things the OS does there is to terminate all other threads. It doesn't call Terminate on them because the OS doesn't know about Delphi classes and methods.
You'll have to do some more debugging to determine why your program doesn't terminate promptly for your customers. You say you can't reproduce the problem in house, and you've written a test program that doesn't exhibit the problem, either. You'll have to find a customer who will cooperate with your further debugging efforts. Do you really know it's the thread that's holding things up, or is that just a guess so far?
Related
Background
My Flagship App seems to leak memory. Well, not really but just during runtime. Investigating this showed the memory 'leak' is resolved when the app closes, but not in between.
I am using a background thread , that iself starts another thread. Both threads (parent and child) are 'Unobserved' which should result in the TOmniTaskControl object to be released and freed when the tread is finished.
Experimenting code
procedure TfrmMain.MyTestProc(const aTask: IOmniTask);
begin
sleep(100);
SGOutputDebugStringFmt('%s.MyTestProc for %s',[ClassName,aTask.Name]);
sleep(100);
end;
procedure TfrmMain.MyNestedTestProc(const aTask: IOmniTask);
var lTask:IOmniTaskControl;
begin
sleep(100);
SGOutputDebugStringFmt('%s.MyTestProc for %s',[ClassName,aTask.Name]);
lTask:=CreateTask(MyTestProc,'NestedTask');
lTask.Unobserved.Run;
sleep(100);
end;
procedure TfrmMain.btSimpleThreadClick(Sender: TObject);
var lTask:IOmniTaskControl;
begin
lTask:=CreateTask(MyTestProc,'SimpleThread');
lTask.Unobserved.Run;
end;
procedure TfrmMain.btNestedThreadClick(Sender: TObject);
var lTask:IOmniTaskControl;
begin
lTask:=CreateTask(MyNestedTestProc,'NestedThread');
lTask.Unobserved.Run;
end;
When debugging, and setting breakpoint on TOmniTaskControl.Destroy, and having a watch TOmniTaskControl.Name on I see the following:
btSimpleThreadCLick:
TOmniTaskControl for 'SimpleThread' gets to be created
TOmniTaskControl for 'SimpleThread' gets to be destroyed
btNestedThreadCLick:
TOmniTaskControl for 'NestedThread' is Created
TOmniTaskControl for 'NestedTask' is Created
TOmniTaskControl for 'NestedThread' is Destroyed
Problem: TOmniTaskControl for 'NestedTask' is NOT destroyed. Another issue is that the OnTerminate isn't called either.
Then, when closing the app, the TOmniTaskCOntrol for 'SimpleThread' is destroyed. (And also, the OnTherminate is fired)
Workaround
I came up with this solution which seems to do the trick. The thing is however, I do usually NOT run my subthreads from a form where a TOmniEventMonitor is at hand. So I'd have to create a global TOmniEventMonitor object for this.
But isn't this the whole point of the UnObserved method?
procedure TfrmMain.MyNestedTestProc(const aTask: IOmniTask);
var lTask:IOmniTaskControl;
begin
sleep(100);
SGOutputDebugStringFmt('%s.MyTestProc for %s',[ClassName,aTask.Name]);
lTask:=CreateTask(MyTestProc,'NestedTask');
lTask.MonitorWith(OmniEventMonitor).Run; // OmniEventMonitor is a component on my form
sleep(100);
end;
Well, secondary workarounbd... kind-of. It does not allow for my thread to be freed unattended.
if I change the NestedTestProc to the code below, then the NestedTaskgets to be destroyed at the expected moment. Unfortunately, this solution is clearly not 'Unobserved'
procedure TfrmMain.MyNestedTestProc(const aTask: IOmniTask);
var lTask:IOmniTaskControl;
begin
sleep(100);
SGOutputDebugStringFmt('%s.MyTestProc for %s',[ClassName,aTask.Name]);
lTask:=CreateTask(MyTestProc,'NestedTask');
try
lTask.Run;
lTask.WaitFor(2000);
finally
lTask:=nil;
end;
sleep(100);
end;
Update 20201029 >>
An invalid handle (1400) error typically occurs when the master task was completed and its associated Monitor was already destroyed.
So - The 'Master' task thread should not die in case there is an "owned" monitor that is monitoring other threads.
So to check this, I changed the timing (using sleep()) to ensure the child task was completed before the master task is completed.
The Invalid handle error is gone now, and the COmniTaskMsg_Terminated message gets to be posted successfully.
But still the ComniTaskMsg_Terminated from the child task is not processed. (I expected the thread of the MasterTask to handle this.)
IMO there are 2 problems:
life time management of the Unobserved monitor
shutdown management of the Unobserved monitor, which should keep the "owning" thread alive and keep processing messages until all
monitored threads/tasks are gone.
Also I wonder whether these shutdownmessages hould be handled/processed by the Application main thread (It seems this is the case now) or otherwise through a separate thread that checks all the monitors in GTaskControlEventMonitorPool . AIA, pretty complicated stuff :s...
Giving this some thought, monitors that were created by the application main thread (thus Monitor.ThreadID=MainThreadID) should be handle their messages in the main thread message loop, and all others probably need to be handled by a separate thread... Its just too confusing! I will see if I write a unit test for this, just to demonstrate what I expect to happen.
<< Update 20201029
The question
With OmniThreadLibrary, How can I use unobserved threads inside threads, and avoid the described memory leak?
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 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.
I have the following scenario:
The program I am writing simulates multiple air networks. Now I have written it so that each simulation is in its own thread with its own data. This is so that there can be no contamination of data and each thread is complete multitasking safe. I have implemented delphi's native TThread class for each thread.
The program has one main thread. It then creates multiple sub threads for each network (my current test case has 6). But each sub thread also has 4 additional threads since its network has more than one layout. So in total I have 31 threads, but only 24 threads actively processing. All threads are created in suspended mode so that I can manually start them.
The computer I am running on is an i5 laptop so it has 4 threads (2 physical cores + 2 HT). In the production environment this will run on servers so they will have more processing power.
Now in the main program thread I have added a break point after the for loop which executes the threads. This doesn't trigger immediately. Watching the debug console of Delphi, it looks like it is only actively running 4 threads at a time. As soon as one exits, it starts another thread.
The simulations in the threads are difficult to optimize and require a few loops to finish the simulation. And each loop requires the previous loop's data. But each simulation is completely independent of the next so the program is an excellent candidate for threads and pipe lining.
Now my question is why does it only start 4 threads and not all the threads as the code specifies?
EDIT Added pieces of code
Main program
for i := 0 to length(Solutions)-1 do
begin
Solutions[i].CreateWorkers; //Setup threads
end;
for i := 0 to length(Solutions)-1 do
begin
Solutions[i].Execute; //start threads
end;
end;
isSolversBusy := false; //breaking point doesn't trigger here
1St level of threads
procedure cSolution.Execute;
var
i : integer;
lIsWorkerStillBusy : boolean;
begin
lIsWorkerStillBusy := true;
for i := 0 to length(Workers)-1 do
begin
Workers[i].Start;
end;
while (lIsWorkerStillBusy) do
begin
lIsWorkerStillBusy := false;
for i := 0 to length(Workers)-1 do
begin
if Workers[i].IsCalculated = false then
begin
lIsWorkerStillBusy := true;
end;
end;
sleep(100);
end;
FindBestNetwork;
IsAllWorkersDone := true;
end;
2nd level of threads
procedure cWorker.Execute;
begin
IsCalculated := false;
Network.UpdateFlows;
Network.SolveNetWork; //main simulation work
CalculateTotalPower;
IsCalculated := true;
end;
EDIT 2
The reason I create them all suspended is because I store them in an array and before I start them I first have create the workers and their properties. I am simulating air network scenarios. Each solution is a different layout, while each worker is a different way of running that layout.
I have to first calculate all the worker's start properties before I start them all. In hind sight I could modify the code to do that in the thread. It currently happens in the main thread. The creation of the threads happens before the piece of code I pasted here.
The reason I keep them all in threads is that I need to evaluate the results of each thread afterwords.
This is your problem :
for i := 0 to length(Solutions)-1 do
begin
Solutions[i].Execute; //start threads
end;
This does not start the threads - this is executing the Execute method in the calling thread. In fact you were not running only 4 threads at a time, you were not even running one - all of this work would be done on the main thread sequentially. To resume a suspended thread you must use Solutions[i].Start.
The Execute method of a TThread is a special method that is executed on the worker thread created by the TThread automatically. When you create a TThread this method is automatically run on the worker thread that the TThread creates. If you create the thread suspended then it simply waits for you to wake the thread before beginning this work. Calling the .Start method of a TThread is what accomplishes this - triggering the underlying worker thread to begin executing the .Execute method.
Otherwise, the Execute method, and all other methods of a TThread are no different from any other normal method belonging to a class. They can be executed on any thread that calls them directly.
In this case, it doesn't seem like you are doing any additional work in the main thread between creating and executing your workers. In this case, unless you have an explicit need for it, you could simply create your threads not-suspended and let them execute automatically upon creation.
I made a syncing thread on my application and I want to know is there a way to let the thread stay open until it finishes it's syncing process, if i close the application form?
Call the thread's WaitFor method in your DPR file, after the Application.Run line. If the thread has already finished running, then WaitFor will return immediately. Otherwise, it will pause the main thread until the other thread terminates. (You'll have to make sure the variable still refers to a valid TThread instance, so don't set its FreeOnTerminate property.)
There are additional things you may wish to consider, beyond the simple issue of allowing the thread to continue running:
Programs only have limited time to stop running if the OS is shutting down or the user is logging out, so you might not have time to wait for synchronization to finish before the OS kills your program.
Users might wonder why the program is still running even though they told it to close, so consider showing some UI while synchronization continues.
Users might not care about synchronization if they're in a hurry and need the program to stop running immediately, so in that UI, you may wish to offer an "abort sync" command.
In the commentary to Rob's excellent answer, the question of showing UI whilst waiting was raised. If you need to show UI whilst waiting, then you will need a more advanced wait that TThread.WaitFor. This is what I use in its place, relying on MsgWaitForMultipleObjects.
procedure WaitUntilSignaled(Handle: THandle; ProcessMessages: Boolean);
begin
if ProcessMessages then begin
Application.ProcessMessages;//in case there are any messages are already waiting in the queue
while MsgWaitForMultipleObjects(1, Handle, False, INFINITE, QS_ALLEVENTS)=WAIT_OBJECT_0+1 do begin
Application.ProcessMessages;
end;
end else begin
WaitForSingleObject(Handle, INFINITE);
end;
end;
....
Form.Show;
WaitUntilSignaled(Thread.Handle, True);
Form.Close;