Recent

Author Topic: TThread - How to stop a thread?  (Read 2306 times)

Slawek

  • New Member
  • *
  • Posts: 43
Re: TThread - How to stop a thread?
« Reply #15 on: February 08, 2023, 09:35:35 pm »
Thank you for your suggestion.
I did this:
Code: Pascal  [Select][+][-]
  1.   TForm1 = class(TForm)
  2.     ...
  3.   private
  4.     ...
  5.     procedure ThreadTerminated(Sender: TObject);
  6.   public
  7.  
  8.   end;
  9.  
  10. procedure TForm1.btStartClick(Sender: TObject);
  11. begin
  12.   ...
  13.   MyThread := TMyThread.Create(true);
  14.   MyThread.FreeOnTerminate := True;
  15.   ...
  16.   MyThread.OnTerminate  := @ThreadTerminated;
  17.   ...
  18.   MyThread.Start;
  19.   ...
  20. end;  
  21.  
  22. procedure TForm1.btStopClick(Sender: TObject);
  23. begin
  24.   MyThread.Terminate;
  25.   MyThread.WaitFor;
  26.   // MyThread := nil; //moved to ThreadTerminated
  27. end;
  28.  
  29. procedure TForm1.FormDestroy(Sender: TObject);
  30. begin
  31.   if Assigned(MyThread) then begin
  32.     MyThread.Terminate;
  33.     MyThread.WaitFor;
  34.   end;
  35. end;
  36.  
  37. procedure TForm1.ThreadTerminated(Sender: TObject);
  38. begin
  39.   MyThread := nil;
  40. end;
  41.  
Thank you. It seems to be working properly. Does not cause errors  :)


alpine

  • Hero Member
  • *****
  • Posts: 1060
Re: TThread - How to stop a thread?
« Reply #16 on: February 09, 2023, 10:04:17 am »
*snip*
Thank you. It seems to be working properly. Does not cause errors  :)
You didn't change it much.

Maybe I wasn't clear enough in the previous post and I need to explain a bit more.

There is an internal procedure named TreadProc which actually is the "main" tread procedure for each TThread.  It receives a reference to the TThread instance, internally calls the the Execute method for that instance, and when that method finishes, if the FreeOnTerminate property value is true, it calls Free method. So the steps performed by TreadProc are actually (assume the name of the reference is Thread):
  • If Thread still not terminated then call Thread.Execute (the thread can be terminated when initially suspended and the Terminate called prior to Start)
  • Set Thread.Finished to true
  • if there is a handler for Thread.OnTerminate then call it
  • if Thread.FreeOnTerminate is true then call Thread.Free
  • End.

Few things should be noted:
  • The TThread.Terminate method just sets the Thread.Terminated property to true. That is the property the Thread.Execute must examine and exit when it becomes true (in a graceful manner)
  • The Thread.Execute may finish prematurely by other means than the above Terminate mechanism, e.g. by unhandled exception. Thus, one should not expect the thread to be finished only after call to Thread.Terminate
  • The above (combined with FreeOnTerminate=True) means that the reference can be freed asynchronously at any time and once started, there is no more guarantees that its reference is a valid one (not freed)
  • At the other hand, the OnTerminate is called in the context of the thread itself, so there the reference is guaranteed to be valid (not yet freed)

To recap, when FreeOnTerminate=True then:
Code: Pascal  [Select][+][-]
  1.   MyThread.Terminate;
isn't safe to call, actually accessing the MyThread isn't safe at all unless you catch everything (try ... except ... end) into the inner loop of MyThread.Execute

Also, when FreeOnTerminate=True and after the:
Code: Pascal  [Select][+][-]
  1.   MyThread.Terminate;
isn't safe to assume anything about the MyThread reference outside the MyThread.Execute and MyThread.OnTerminate handler body.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.btStopClick(Sender: TObject);
  2. begin
  3.   MyThread.Terminate; // No more asumptions about validity of MyThread after that point
  4.   MyThread.WaitFor; // <--- Here the ThreadProc(MyThread) may already have freed the MyThread reference !!!
  5.                     //      As a pure luck, the memory contents there may still hold the old values for MyThread
  6.   // MyThread := nil; //moved to ThreadTerminated
  7. end;

And that it doesn't cause errors now on your computer does not mean it won't make your life miserable later.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

 

TinyPortal © 2005-2018