AlexV
AlexV

Reputation: 23098

What's the correct way in Delphi to check if a non-Suspended created TThread with FreeOnTerminate = True is still executing?

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

Answers (1)

Dalija Prasnikar
Dalija Prasnikar

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

Related Questions