1

我一直在寻找一种方法来监视 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;

在我的应用程序中,我需要按需启动和停止该线程,但上面的代码不允许这样做。仅设置 Terminated 标志是行不通的。

以某种方式告诉线程停止等待,然后释放它并在需要时创建一个新线程就足够了。如何更改此代码以实现此目的?

4

3 回答 3

8

WaitForMultipleObjects()与包含两个事件的数组一起使用,而不是WaitForSingleObject(). 向线程类添加手动重置事件,并在设置TerminatedTrue. 检查两个事件中的哪一个已发出信号的返回值,并采取相应措施。

编辑:

一些最小的 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;

您只需要将问题中的代码添加到其中。简单地尝试释放线程实例将做所有必要的事情来解除线程阻塞(如果需要)。

于 2009-11-14T16:12:15.050 回答
1

相反,在 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 现在承认它们不安全使用。请参阅Delphi-2010 中不推荐使用 TThread.resume 应该在什么地方使用?http://msdn.microsoft.com/en-us/library/ms686345%28VS.85%29.aspx

于 2009-11-14T16:03:25.427 回答
1

这行得通,只需进行如下的小改动,现在当您调用 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;
于 2009-11-14T19:01:58.763 回答