How to start/stop a monitoring Delphi thread on demand?
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 开发者_如何学JAVAand 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?
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).
Instead in INFINITE you should have WaitForSingleObject time out after a period. That way the loop continues and Terminated is checked.
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;
The methods TThread.Suspend and TThread.Resume could theoretically be used to temporary stop threads, but as Delphi 2010 now acknowledges they are not safe for use. See TThread.resume is deprecated in Delphi-2010 what should be used in place? and http://msdn.microsoft.com/en-us/library/ms686345%28VS.85%29.aspx
This works, just make small changes as below and now when you call 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;
精彩评论