Как запустить / остановить поток мониторинга Delphi по требованию?
-
20-09-2019 - |
Вопрос
Я искал способ отслеживать конкретные изменения реестра в 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 не подойдет.
Было бы достаточно каким-то образом сообщить потоку прекратить ожидание, затем освободить его и создать новый, когда это необходимо.Как я могу изменить этот код, чтобы достичь этого?
Решение
Использование WaitForMultipleObjects()
с массивом из двух событий вместо WaitForSingleObject()
.Добавьте событие ручного сброса в класс thread и просигнализируйте о нем после того, как вы установили Terminated
Для True
.Проверьте возвращаемое значение, о каком из двух событий был подан сигнал, и действуйте соответствующим образом.
Редактировать:
Некоторый минимальный код Delphi 2009 для демонстрации идеи.Вы должны добавить SyncObjs
к списку используемых единиц измерения и добавьте
fTerminateEvent: TEvent;
к тому private
раздел вашего класса thread.
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;
Вам нужно только добавить к нему код из вашего вопроса.Простая попытка освободить экземпляр потока сделает все необходимое для разблокировки потока (при необходимости).
Другие советы
Вместо этого в 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.Приостановить и TThread.Возобновить теоретически можно было бы использовать для временной остановки потоков, но, как теперь признает Delphi 2010, они небезопасны для использования.Видишь TThread.resume устарел в Delphi-2010, что следует использовать на месте? и http://msdn.microsoft.com/en-us/library/ms686345%28VS.85%29.aspx
Это работает, просто внесите небольшие изменения, как показано ниже, и теперь, когда вы вызываете 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;