Как реализовать поток, который периодически что-то проверяет, используя минимальные ресурсы?

Я хотел бы иметь поток, работающий в фоновом режиме, который будет проверять подключение к некоторому серверу с заданным интервалом времени. Например, каждые 5 секунд.

Я не знаю, есть ли хороший "шаблон дизайна" для этого? Если я правильно помню, я где-то читал, что спящий поток в его методе execute не хорош. Но я могу ошибаться.

Кроме того, я мог бы использовать обычный класс TThread или библиотеку потоков OTL.

Есть идеи?

Благодарю.

5 ответов

Решение

Вы можете использовать событие и реализовать Execute метод TThread потомок по петле с WaitForSingleObject ожидание события с указанием таймаута. Таким образом, вы можете разбудить поток сразу же при необходимости, например, при завершении.

В OmniThreadLibrary вы бы сделали:

uses
  OtlTask,
  OtlTaskControl;

type
  TTimedTask = class(TOmniWorker)
  public
    procedure Timer1;
  end;

var
  FTask: IOmniTaskControl;

procedure StartTaskClick;
begin
  FTask := CreateTask(TTimedTask.Create())
    .SetTimer(1, 5*1000, @TTimedTask.Timer1)
    .Run;
end;

procedure StopTaskClick;
begin
  FTask.Terminate;
  FTask := nil;
end;

procedure TTimedTask.Timer1;
begin
  // this is triggered every 5 seconds
end;

Что касается сна в Execute - это зависит от того, как вы это делаете. Если вы используете Sleep, то это может быть не очень разумно (например, потому что это предотвратит остановку потока во время сна). Спать с WaitForSingleObject нормально.

Пример TThread и WaitForSingleObject:

type
  TTimedThread = class(TThread)
  public
    procedure Execute; override;
  end;

var
  FStopThread: THandle;
  FThread: TTimedThread;

procedure StartTaskClick(Sender: TObject);
begin
  FStopThread := CreateEvent(nil, false, false, nil);
  FThread := TTimedThread.Create;
end;

procedure StopTaskClick(Sender: TObject);
begin
  SetEvent(FStopThread);
  FThread.Terminate;
  FThread.Free;
  CloseHandle(FStopThread);
end;

{ TTimedThread }

procedure TTimedThread.Execute;
begin
  while WaitForSingleObject(Form71.FStopThread, 5*1000) = WAIT_TIMEOUT do begin
    // this is triggered every 5 seconds
  end;
end;

Реализация таймера OTL аналогична приведенному выше коду TThread. Таймеры OTL хранятся в списке приоритетов (в основном таймеры сортируются по времени "следующего вхождения"), а внутренний диспетчер MsgWaitForMultipleObjects в TOmniWorker указывает соответствующее значение времени ожидания для таймера с самым высоким приоритетом.

Если поток работает в течение всего жизненного цикла приложения, он может быть просто остановлен операционной системой при закрытии приложения и не требует точного хронометража, зачем беспокоиться о решениях, которые требуют больше ввода, чем сна (5000)?

Чтобы добавить другое средство достижения 5-секундного события, можно использовать мультимедийный таймер, который похож на TTimer, но не зависит от вашего приложения. После его настройки (вы можете настроить однократный или повторяющийся) он перезвонит вам в другой поток. По своей природе это очень точно (с точностью до 1 мс). Посмотрите пример кода Delphi здесь.

Код для вызова таймера прост и поддерживается на всех платформах Windows.

Используйте CreateWaitableTimer и SetWaitableTimer

Другие вопросы по тегам