Pytanie Jak zaimplementować wątek, który okresowo sprawdza coś przy użyciu minimalnych zasobów?


Chciałbym mieć wątek działający w tle, który sprawdzi połączenie z jakimś serwerem z określonym przedziałem czasu. Na przykład co 5 sekund.

Nie wiem, czy istnieje dobry "wzór desing" dla tego? Jeśli dobrze pamiętam, przeczytałem gdzieś, że śpiworzenie w jego metodzie wykonania nie jest dobre. Ale mogę się mylić.

Ponadto mógłbym używać normalnej biblioteki wątków TThread lub OTL.

Jakieś pomysły?

Dzięki.


13
2017-12-07 08:22


pochodzenie




Odpowiedzi:


Możesz użyć zdarzenia i wdrożyć Execute metoda TThread potomek przez pętlę z WaitForSingleObject oczekiwanie na wydarzenie, określenie czasu oczekiwania. W ten sposób możesz natychmiast obudzić nić, gdy jest taka potrzeba, np. po zakończeniu.


14
2017-12-07 08:38





W OmniThreadLibrary, ty byś zrobił:

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;

Jeśli chodzi o spanie w Execute - to zależy od tego, jak to zrobisz. Jeśli używasz trybu uśpienia, może to nie być bardzo mądre (na przykład, ponieważ zapobiegnie to zatrzymaniu nici podczas snu). Spanie z WaitForSingleObject jest w porządku.

Przykład TThread i 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;

Implementacja zegara OTL jest podobna do powyższego kodu TThread. Liczniki czasu OTL są przechowywane na liście priorytetów (w zasadzie liczniki są sortowane według czasu "następnego wystąpienia"), a wewnętrzne narzędzie MsgWaitForMultipleObjects w TOmniWorker określa odpowiednią wartość limitu czasu dla zegara o najwyższym priorytecie.


16
2017-12-07 09:09



Dzięki gabr, chciałem cię o to zapytać na twoim forum, ale to nie działa. +1 - Wodzu
Tak, coś jest nie tak z serwerem domeny; Pracuję nad tym. - gabr
Czy możesz powiedzieć nam więcej, w jaki sposób wdrożyłeś swoje rozwiązanie dotyczące pomiaru czasu? Może napisać o tym blogu? :) - Wodzu
@Wodzu, odpowiedział wyżej - gabr
Dziękuję Ci. Będę zagłębiać się w więcej informacji na forum, gdy będzie on działał. - Wodzu


Jeśli wątek działa przez cały czas użytkowania aplikacji, może być po prostu zakończony przez system operacyjny przy zamknięciu aplikacji i nie potrzebuje dokładnego harmonogramu, po co zawracać sobie głowę rozwiązaniami wymagającymi większego pisania niż sen (5000)?


1
2017-12-07 10:08



Ponieważ możesz wtedy czekać do 5 sekund, aż wątek (i przez to program) się zakończy. - gabr
Ja, po prostu przesypiam wątki na 1000ms, i budzi się, sprawdza, czy zakończone, a następnie sprawdza, czy praca ma być wykonywana. Wątek jest prawie bezczynny w systemie i działa dobrze i wydajnie. Oczywiście, opóźnienie 1 sekundy na zamknięciu, ale ja nazywam Terminate na wszystkich wątkach jako pierwszą, a następnie na WaitFors, więc całkowite opóźnienie wszystkich wątków wynosi 1sec, a nie 1sec razy liczba wątków. Ponieważ niektóre wątki będą działać dłużej, koszt jest znikomy. - mj2008
@Torbins itp. - jeśli nie zniszczysz nici, dtor nie zostanie wywołany i nie będzie czekać! - Martin James
@gabr możesz czekać, jasne, ale dlaczego chcesz? Czy nie chcesz natychmiast wyłączyć aplikacji? - Martin James
TThread.Terminate po prostu ustawia flagę, AFAIK, więc nie może powodować opóźnień. Przyczyną opóźnienia jest TThread.WaitFor, jawnie lub tak zwane z TThread.Destroy. Jeśli nie zadzwonisz do TThread.WaitFor, Twoja aplikacja nie zostanie zablokowana. - Martin James


Aby dodać inny sposób osiągnięcia zdarzenia 5-sekundowego, można skorzystać z funkcji Multimedia Timer, która jest podobna do TTimer, ale nie ma zależności od aplikacji. Po jej skonfigurowaniu (możesz ustawić jednorazowe lub powtarzalne), oddzwoni do innego wątku. Ze swej natury jest bardzo dokładny (z dokładnością do 1ms). Zobacz niektóre przykładowy kod Delphi tutaj.

Kod wywoływania licznika czasu jest prosty i jest obsługiwany na wszystkich platformach Windows.


1
2017-12-07 10:11





Posługiwać się CreateWaitableTimer i SetWaitableTimer


0
2017-12-07 09:23



Dlaczego jest to lepsze / gorsze od omawianych tutaj rozwiązań? (IOW, powinieneś poprawić swoją odpowiedź). - gabr