Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I stop a thread before it's finished running?

I have a thread called TMyThread and I overrode the Execute procedure like this:

 procedure TMyThread.Execute;
 Begin
     repeat
     //Some Work 
     Sleep(5000);
     //Some Work 2;
     Sleep(5000); 
     until FActive=False
 End;

In the main form I have button called 'Destroy My Thread'. I want to destroy my thread but the problem is that my thread will be destroyed only if it finish its work. I want to destroy my thread even it has not finished its work. How do I do this?

like image 322
S.MAHDI Avatar asked Nov 29 '22 02:11

S.MAHDI


1 Answers

Your thread's Execute method must regularly check the state of the thread's Terminated property. And if it is True, then the thread Execute method must exit.

So, a typical Execute method might look like this:

procedure TMyThread.Execute;
begin
  while not Terminated do
    DoNextPieceOfWork
end;

It looks like your thread has its own FActive flag that is performing the same task. The problem with that is that TThread doesn't know about it. So you should get rid of FActive and instead use the built in mechanism.

When you call Free on the thread it will call Terminate. That sets Terminated to be True. Then it waits for the thread method to exit. That will happen because your thread notices that Terminated is True and quits. And then the thread's destructor can continue and finish the job of tidying up the thread.


Looking at the code in your answer, it would be better written to make use of the existing Terminate mechanism.

type
  TMyThread = class(TThread)
  private
    FTerminateEvent: TEvent;
  protected
    procedure Execute; override;
    procedure TerminatedSet; override;
  public
    constructor Create(ACreateSuspended: Boolean);
    destructor Destroy; override;
  end;

constructor TMyThread.Create(ACreateSuspended: Boolean);
begin
  inherited Create(ACreateSuspended);
  FTerminateEvent := TEvent.Create(nil, True, False, '');
end;

destructor TMyThread.Destroy;
begin
  inherited;
  FTerminateEvent.Free;
end;

procedure TMyThread.TerminatedSet;
begin
  FTerminateEvent.SetEvent;
end;

procedure TMyThread.Execute;
begin
  while not Terminated do
  begin
    // do somthing interesting!
    FTerminateEvent.WaitFor(5000);
  end;
end;

Now there's no need for a separate Stop method. You can just call Free on the thread. Then Terminate is called. And then TerminatedSet is called. Then the event is signaled. Then Execute terminates. And then the thread can go away.


Having said that, I'm struggling to think of a scenario where a 5000ms timeout would be the best approach. I don't know why you are doing this, but I'd guess that you are trying to throttle the thread so that it doesn't run hot. You want to avoid a busy loop. That's admirable, but using a fixed timeout is not the way to do it. The way to do it is to wait on a synchronisation event, typically an event. Then when there is more work to be done, the event becomes signaled and your thread wakes up.

like image 156
David Heffernan Avatar answered Dec 05 '22 09:12

David Heffernan