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?
Once ExitProcess() is called, the OS will stop ALL threads of the process, no matter what state they are in, before deallocating any memory, (like the memory containing your flag). The thread that calls ExitProcess() never has control returned to it.
Foreground threads are those threads that keep running even after the application exits or quits. It has the ability to prevent the current application from terminating.
By default, threads are foreground threads, meaning they keep the application alive for as long as any one of them is running.
In programming, a background thread is a thread that runs behind the scenes, while the foreground thread continues to run. For instance, a background thread may perform calculations on user input while the user is entering information using a foreground thread.
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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With