Как запустить / остановить поток мониторинга Delphi по требованию? - PullRequest
1 голос
/ 14 ноября 2009

Я искал способ следить за конкретными изменениями реестра в Delphi. Нашел решение на about.com:

procedure TRegMonitorThread.Execute;
begin
  InitThread; // method omitted here
  while not Terminated do
  begin
    if WaitForSingleObject(FEvent, INFINITE) = WAIT_OBJECT_0 then
    begin
      fChangeData.RootKey := RootKey;
      fChangeData.Key := Key;
      SendMessage(Wnd, WM_REGCHANGE, RootKey, LongInt(PChar(Key)));
      ResetEvent(FEvent);

      RegNotifyChangeKeyValue(FReg.CurrentKey, 1, Filter, FEvent, 1);
    end;
  end;
end;

В моем приложении мне нужно будет запускать и останавливать этот поток по требованию, но приведенный выше код этого не позволяет. Установка флага «Завершение» не поможет.

Было бы достаточно как-то сказать потоку перестать ждать, затем освободить его и создать новый, когда это необходимо. Как я могу изменить этот код для достижения этого?

Ответы [ 3 ]

8 голосов
/ 14 ноября 2009

Используйте WaitForMultipleObjects() с массивом из двух событий вместо WaitForSingleObject(). Добавьте событие ручного сброса в класс потока и сообщите об этом после того, как вы установили Terminated в True. Проверьте возвращаемое значение, о каком из двух событий было сообщено, и действуйте соответственно.

Edit:

Небольшой минимальный код Delphi 2009 для демонстрации идеи. Вы должны добавить SyncObjs в список используемых единиц и добавить

  fTerminateEvent: TEvent;

в раздел private вашего класса потока.

constructor TTestThread.Create;
begin
  inherited Create(TRUE);
  fTerminateEvent := TEvent.Create(nil, True, False, '');
  // ...
  Resume;
end;

destructor TTestThread.Destroy;
begin
  fTerminateEvent.SetEvent;
  Terminate; // not necessary if you don't check Terminated in your code
  WaitFor;
  fTerminateEvent.Free;
  inherited;
end;

procedure TTestThread.Execute;
var
  Handles: array[0..1] of THandle;
begin
  Handles[0] := ...; // your event handle goes here
  Handles[1] := fTerminateEvent.Handle;
  while not Terminated do begin
    if WaitForMultipleObjects(2, @Handles[0], False, INFINITE) <> WAIT_OBJECT_0 then
      break;
    // ...
  end;
end;

Вам нужно только добавить к нему код в вашем вопросе. Простая попытка освободить экземпляр потока сделает все необходимое, чтобы разблокировать поток (при необходимости).

1 голос
/ 14 ноября 2009

Это работает, просто внесите небольшие изменения, как показано ниже, и теперь, когда вы вызываете Terminate:

  TRegMonitorThread = class(TThread)
  ...
  public
    procedure Terminate; reintroduce;
...

procedure TRegMonitorThread. Terminate;  // add new public procedure
begin
  inherited Terminate;
  Windows.SetEvent(FEvent);
end;

procedure TRegMonitorThread.Execute;
begin
  InitThread;

  while not Terminated do
  begin
    if WaitForSingleObject(FEvent, INFINITE) = WAIT_OBJECT_0 then
    begin
      if Terminated then // <- add this 2 lines
        Exit;
      ...
    end;
  end;
end;
1 голос
/ 14 ноября 2009

Вместо этого в INFINITE у вас должен быть тайм-аут WaitForSingleObject после периода. Таким образом, цикл продолжается, и проверяется, завершен ли он.

procedure TRegMonitorThread.Execute;
begin
  InitThread; // method omitted here
  while not Terminated do
  begin
    if WaitForSingleObject(FEvent, 1000) = WAIT_OBJECT_0 then
    begin
      fChangeData.RootKey := RootKey;
      fChangeData.Key := Key;
      SendMessage(Wnd, WM_REGCHANGE, RootKey, LongInt(PChar(Key)));
      ResetEvent(FEvent);

      RegNotifyChangeKeyValue(FReg.CurrentKey, 1, Filter, FEvent, 1);
    end;
  end;
end;

Методы TThread.Suspend и TThread.Resume теоретически могут использоваться для временной остановки потоков, но, как теперь признает Delphi 2010, они небезопасны для использования. См. TThread.resume устарел в Delphi-2010, что следует использовать на месте? и http://msdn.microsoft.com/en-us/library/ms686345%28VS.85%29.aspx

...