重新启动 TEvent.WaitFor 而不退出

Restart TEvent.WaitFor without exiting it

是否可以在不退出的情况下重新启动TEvent.WaitFor?当 _interval 被 setter 更改时,我需要重新开始等待。比如设置了一个小时,我想把间隔时间改成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;

目前我"solved"通过以下方式解决问题:

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 时,"set" 状态被永久保存并且 CPU 使用率跃升至 100%。

最后我使用了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;

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

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;