How to start/stop a monitoring Delphi thread on demand?

Marek Jedliński picture Marek Jedliński · Nov 14, 2009 · Viewed 9.6k times · Source

I've been looking for a way to monitor for specific registry changes in Delphi. Found a solution at 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;

In my application I will need to start and stop this thread on demand, but the above code does not permit that. Just setting the Terminated flag won't do.

It would be sufficient to somehow tell the thread to stop waiting, then free it and create a new one when needed. How can I change this code to achieve that?

Answer

mghie picture mghie · Nov 14, 2009

Use WaitForMultipleObjects() with an array of two events instead of WaitForSingleObject(). Add a manual reset event to the thread class, and signal it after you have set Terminated to True. Check the return value which of the two events has been signalled, and act accordingly.

Edit:

Some minimal Delphi 2009 code to demonstrate the idea. You have to add SyncObjs to the list of used units, and add

  fTerminateEvent: TEvent;

to the private section of your thread class.

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;

You only need to add the code in your question to it. Simply trying to free the thread instance will do everything necessary to unblock the thread (if necessary).