Reputation: 23098
So I created a class: TWorkerThread = class(TThread)
. In the TWorkerThread
Constructor I set FreeOnTerminate := True
.
I then added a private Worker
variable to my TForm
. When I instantiate TWorkerThread
for Worker
, I always use CreateSuspended := False
.
The only interactions I need to have with the thread once started is to cancel the process (Terminate
) and check if the process is over (that check is user event based).
To check if the thread is still active I do:
if (Self.Worker <> nil) and (not Self.Worker.Finished) then
//Still active
Which seems to work fine but I fear that at some time the TWorkerThread
will be <> nil
but still be .free
(remember that TWorkerThread
are all FreeOnTerminate := True
). When a TWorkerThread
"self destroy" once terminated do they always become nil
? If not, how do I handle that (to avoid access violations)?
Another thing, if a Worker
is active on it's form destroy, I do:
if (Self.Worker <> nil) and (not Self.Worker.Finished) then
begin
Self.Worker.Terminate;
//wait here for completion
end;
After the Terminate
if I do a WaitFor
I get an access violation (because of the self destroy). Should I enforce a wait here? Whats the best way to do it?
This is my first proper usage of TThread in Delphi. The code above is a skimmed down version of what I do for clarity.
Upvotes: 3
Views: 687
Reputation: 28516
What's the correct way in Delphi to check if a non-Suspended created TThread with FreeOnTerminate = True is still executing?
There is none.
As soon as self destroying thread is started, you are no longer allowed to touch reference holding such thread from outside code, because that reference can become stale at any time.
Under some circumstances (if logic handling OnTerminate
call in DoTerminate
is not modified in your custom thread class) you can safely access thread reference from OnTerminate
event handler. However, that will not help you solve your problem because you cannot wait on self destroying thread. On Windows such code causes exception that can be caught and handled, but on other platforms it causes undefined behavior and random deadlocks. But even on Windows writing such code is not advisable.
If you need to check status of a thread, cancel it or wait for it, you cannot use self destroying threads. Period. (For those that may object previous statement, yes under some circumstances some of those things are possible, but they commonly lead to shooting yourself in the foot).
You can use following workflow with TWorkerThread
, providing that you leave FreeOnTerminate
on False
in your thread constructor and construct unsuspended thread. All following methods must be called from the main thread to avoid race condition on Worker
reference. Having one extra thread that does nothing while your form is open will not cause you any trouble.
procedure TMyForm.StartWorker;
begin
FreeAndNil(Worker); // or if Assigned(Worker) then Exit;
Worker := TWorkerThread.Create;
end;
procedure TMyForm.CancelWorker;
begin
// this will initiate thread termination and waiting
FreeAndNil(Worker);
end;
procedure TMyForm.Destroy;
begin
// technically we could only use Free here,
// but since other code requires niling the reference this is more consistent
FreeAndNil(Worker);
inherited;
end;
Upvotes: 3