重新启动TEvent.WaitFor而不退出它

时间:2019-09-10 08:38:55

标签: multithreading wait delphi-10-seattle

是否可以在不退出TEvent.WaitFor的情况下重新启动它?当设置者更改_interval时,我需要重新开始等待。例如,当设置为一小时,而我想将间隔更改为15秒时,更改将在经过一小时后生效。

_terminatingEvent: TEvent;

procedure TTimerThread.Execute();
begin
  inherited;
  while not Terminated do begin
    try
      _terminatingEvent.WaitFor(_interval);
      if Assigned(_onTimer) and _enabled then _onTimer(Self);
    except
      on ex: Exception do _logError(ex);
    end;
  end;
end;

procedure TTimerThread.TerminatedSet();
begin
  _terminatingEvent.SetEvent();
end;

procedure TTimerThread._setInterval(const Value: Integer);
begin
  _interval := Value;
  //Restart WaitFor here
end;

目前,我通过以下方式“解决”了该问题:

procedure TTimerThread.Execute();
begin
  inherited;
  while not Terminated do begin
    try
      if _terminatingEvent.WaitFor(_interval) = wrTimeout then
        if Assigned(_onTimer) and _enabled then _onTimer(Self);
    except
      on ex: Exception do _logError(ex);
    end;
  end;
end;

procedure TTimerThread.TerminatedSet();
begin
  _terminatingEvent.SetEvent();
end;

procedure TTimerThread._setInterval(const Value: Integer);
begin
  _interval := Value;
  _terminatingEvent.ResetEvent();
end;

似乎当我使用SetEvent而不是ResetEvent时,“设置”状态会永久保存,CPU使用率会跃升到100%。

2 个答案:

答案 0 :(得分:1)

您可以使用两个TEvent对象,一个用于计时器,一个用于设置器,例如:

type
  TTimerThread = class(TThread)
  private
    _terminatingEvent: TEvent;
    _updatedEvent: TEvent;
    ...
  protected
    procedure Execute; override;
    procedure TerminatedSet; override;
  public
    constructor Create(ASuspended: Boolean); reintroduce;
    destructor Destroy; override;
  end;

constructor TTimerThread.Create(ASuspended: Boolean);
begin
  inherited Create(ASuspended);
  _terminatingEvent := TEvent.Create(nil, True, False, '');
  _updatedEvent := TEvent.Create(nil, False, False, '');
end;

destructor TTimerThread.Destroy;
begin
  _terminatingEvent.Free;
  _updatedEvent.Free;
  inherited;
end;

procedure TTimerThread.Execute;
var
  Arr: THandleObjectArray;
  SignaledObj: THandleObject;
begin
  SetLength(Arr, 2);
  Arr[0] := _terminatingEvent;
  Arr[1] := _updatedEvent;

  while not Terminated do
  begin
    try
      case THandleObject.WaitForMultiple(Arr, _interval, False, SignaledObj) of
        wrSignaled: begin
          if (SignaledObj is TEvent) then (SignaledObj as TEvent).ResetEvent();
        end;
        wrTimeOut: begin
          if Assigned(_onTimer) and _enabled then
            _onTimer(Self);
        end;
        wrError: begin
          RaiseLastOSError;
        end;
      end;
    except
      on ex: Exception do
        _logError(ex);
    end;
  end;
end;

procedure TTimerThread.TerminatedSet;
begin
  inherited;
  _terminatingEvent.SetEvent;
end;

procedure TTimerThread._setInterval(const Value: Integer);
begin
  if _interval <> Value then
  begin
    _interval := Value;
    _updatedEvent.SetEvent;
  end;
end;

答案 1 :(得分:0)

最后,我使用了SetEventResetEvent的组合。如果有人有更好的答案,我会接受的。

procedure TTimerThread.Execute();
begin
  inherited;
  while not Terminated do begin
    try
      case _terminatingEvent.WaitFor(_interval) of
        wrTimeout: if Assigned(_onTimer) and _enabled then _onTimer(Self);
        wrSignaled: _terminatingEvent.ResetEvent();
      end;
    except
      on ex: Exception do _logError(ex);
    end;
  end;
end;

procedure TTimerThread.TerminatedSet();
begin
  _terminatingEvent.SetEvent();
end;

procedure TTimerThread._setInterval(const Value: Integer);
begin
  _interval := Value;
  _terminatingEvent.SetEvent();
end;

procedure TTimerThread._setEnabled(const Value: Boolean);
begin
  if _enabled = Value then Exit();
  _enabled := Value;
  if _enabled and Suspended then Suspended := False;
  _terminatingEvent.SetEvent();
end;