I have a TThread object and want to be able to start/stop the thread via a button on the main form of the program. I've been looking into ways to do this and so far I have the following ideas:
- Terminate and Free the thread when the user clicks stop and create a new one when they click start.
- Use sleep to delay the thread (I don't want to do this)
- Have a property that is a boolean to determine if the thread is paused or not. The code in the Execute will only happen if this boolean is false.
I'm leaning towards #3. Would setting a boolean property on the TThread object from the main form be threadsafe?
Which of these options, or any better alternative, should I go with? This is my first time using threads so any help is appreciated.
1.Terminate and Free the thread when the user clicks stop and create a new one when they click start.
This is certainly an option, if the overhead is minimal.
3.Have a property that is a boolean to determine if the thread is paused or not. The code in the Execute will only happen if this boolean is false.
You could do that, but you would have to check that boolean regularly and if set then enter a wait loop until either it is cleared or the thread is signaled to terminate.
Would setting a boolean property on the TThread object from the main form be threadsafe?
It is as thread-safe as calling TThread.Terminate()
, which simply sets the boolean TThread.Terminated
property.
Which of these options, or any better alternative, should I go with?
I use option #4 - using signaled events instead of booleans. For example:
type
TMyThread = class(TThread)
private
FRunEvent, FTermEvent: TEvent;
FWaitEvents: THandleObjectArray;
procedure CheckPause;
protected
procedure Execute; override;
procedure TerminatedSet; override;
public
constructor Create; reintroduce;
destructor Destroy; override;
procedure Pause;
procedure Unpause;
end;
constructor TMyThread.Create;
begin
inherited Create(False);
FRunEvent := TEvent.Create(nil, True, True, '');
FTermEvent := TEvent.Create(nil, True, False, '');
SetLength(FWaitEvents, 2);
FWaitEvents[0] := FRunEvent;
FWaitEvents[1] := FTermEvent;
end;
destructor TMyThread.Destroy;
begin
FRunEvent.Free;
FTermEvent.Free;
inherited;
end;
procedure TMyThread.Execute;
begin
while not Terminated do
begin
// do some work...
CheckPause;
// do some more work...
CheckPause;
// do some more work...
CheckPause;
//...
end;
end;
procedure TMyThread.TerminatedSet;
begin
FTermEvent.SetEvent;
end;
procedure TMyThread.CheckPause;
var
SignaledEvent: THandleObject;
begin
while not Terminated do
begin
case TEvent.WaitForMultiple(FWaitEvents, INFINITE, False, SignaledEvent) of
wrSignaled: begin
if SignaledEvent = FRunEvent then Exit;
Break;
end;
wrIOCompletion: begin
// retry
end;
wrError: begin
RaiseLastOSError;
end;
end;
SysUtils.Abort;
end;
procedure TMyThread.Pause;
begin
FRunEvent.ResetEvent;
end;
procedure TMyThread.Unpause;
begin
FRunEvent.SetEvent;
end;
Check out the wiki for Delphi on starting and stopping threads here: http://docwiki.embarcadero.com/RADStudio/Berlin/en/Starting_and_Stopping_Threads
This is applicable as far back as Delphi 7. It may be applicable farther back, but I cannot confirm earlier versions.
来源:https://stackoverflow.com/questions/38516091/pausing-a-thread-with-a-property