2011-12-08 15 views
6

Mam dll zużyte przez usługę. Jego podstawowym zadaniem jest uruchamianie co X minut i przeprowadzanie niektórych kontroli systemu. W mojej dll mam klasę najwyższego poziomu, która deklaruje System.threading.timer i Timercallback. Konstruktor klasy inicjalizuje timerCallback z moją funkcją wątku. W moim Handler "Onstart" uruchamiam timer z timercallback i ustawić następnym razem na ogień i interwał czasowy. W moim przypadku jest to co 10 minut. Zwykle podczas tych 10-minutowych kontroli nie ma nic do zrobienia, ale usługa jest zmuszana do zrobienia czegoś co najmniej raz dziennie o ustalonym czasie.C# .NET 2 Threading.Timer - czas dryfujący

Mój problem: Zauważyłem, że podczas testowania czas codziennej kontroli wykonywany jest codziennie, powoli odsuwając się od pożądanego czasu rozpoczęcia o godzinie 8.30. na przykład przez około 20 dni nieparzystych mój czas upłynął od 08.30 do 08.31.35. Dryfuje około 4 - 6 sekund każdego dnia.

Moje pytanie: czy ktoś wie, dlaczego ten czas tak dryfuje i jak mogę go utrzymać w wyznaczonym czasie?

dziękuję

+0

Nie jestem pewien na podstawie tego pytania, ale wydaje się, że czytasz, że planujesz timer, aby coś zrobił w 10 minut, którego pierwszą akcją jest zaplanowanie kolejnego zadania w 10 minut. Nie planowanie zadania co 10 minut. Czy mógłbyś opublikować kod czasowy? – Sign

+0

Ustawiam timer tylko raz, gdy usługa zostanie uruchomiona po raz pierwszy i ustawię ją na uruchamianie co 10 minut. To tak, jakby co 10 minut "tykać" od tego czasu stopniowo dryfuje później i później. Trudno jest dodać kod, bo nie mogę umieścić zwrotu karetki w tym polu texc! – Keith

+0

@Keith - Nie powinieneś mieć pełnego dylusa przez komentarze. Jeśli masz kod do przesłania, zaktualizuj swoje pytanie. –

Odpowiedz

6

Czas „dryfuje”, bo zegar nie jest po prostu, że precyzyjny. Jeśli trzeba uruchomić swój kod jak najbliżej do pewnego przedziału, można zrobić coś takiego:

public void MyTimerCallback(object something) { 
    var now = DateTime.UtcNow; 
    var shouldProbablyHaveRun = new DateTime(
     now.Year, now.Month, now.Day, 
     now.Hour, now.Minute - (now.Minute % 10), 0); 
    var nextRun = shouldProbablyHaveRun.AddMinutes(10.0); 

    // Do stuff here! 

    var diff = nextRun - DateTime.UtcNow; 
    timer.Change(diff, new TimeSpan(-1)); 
} 

... zakładając używasz instancji System.Threading.Timer. Zmodyfikuj przykład, jeśli używasz innego typu timera (jest ich kilka!).

+0

To wygląda dobrze dzięki. Używam system.threading.timer czy lepiej byłoby zmienić na Timers.Timer? – Keith

+1

Zaktualizowałem moją odpowiedź na użycie 'System.Threading.Timer'. Różne zegary mają korzyści w różnych sytuacjach. Jeśli czasomierz pracował dla Ciebie do tej pory, nie ma powodu do zmiany. Zaakceptuj moją odpowiedź jako odpowiedź, jeśli działa ona dla Ciebie. :) –

+0

@Keith: 'System.Timers.Timer' faktycznie używa' System.Threading.Timer' za kulisami. Więc tak naprawdę nie ma to znaczenia, ale ten pierwszy jest nieco wygodniejszy w użyciu. –

0

Dlaczego nie sprawdzać co minutę, jeśli czynność należy wykonać?

tj:

if (DateTime.Now.Minute % 10) == 0 
+0

Jeśli licznik uruchomi się o godzinie 6:01, co 10 minut, gdy sprawdzi, "DateTime.Now.Minute% 10" nie będzie miało wartości "0" :) –

0

trwa skończoną ilość czasu, aby wykonać operacje robisz w swoim timera metody obsługi, więc ma to sens, że nie wydarzy się co 10 minut do drugiego, zwłaszcza jeśli planujesz kolejne przebudzenie po wykonaniu czeków i tym podobnych. jeśli mimo to sprawdzasz, czy odpowiadasz, czy jest to pora na zrobienie tego, powinieneś częściej uruchamiać timer, aby zaspokoić potrzebną rozdzielczość i zaufać twojemu sprawdzeniu, kiedy należy wykonać coś, aby upewnić się, że tak jest. prawdopodobnie potrzebujesz jakiegoś utrwalacza, aby upewnić się, że nie zostanie wykonany dwukrotnie (jeśli jest to ważne) w przypadku, gdy nastąpi zamknięcie/ponowne uruchomienie, a stan wiedzy o tym, czy został już uruchomiony, nie jest nadal w pamięci.

+0

Cześć dave, sposób, w jaki zrozumiałem, że zdarzenie zegara być odpalanym co x minut, gdy tylko go skonfiguruję. praca, którą robię w moim wątku, jest oddzielna od tej? więc czy zakończyłem moją pracę, czy nie, następne 10 minutowe wydarzenie przyjdzie w każdym razie. W tym celu mam zmienną "zajęty", którą ustawiłem i tylko "działam", jeśli jeszcze nie jest zajęty. Jeśli to ma sens? – Keith

+0

zależy to od używanego zegara i sposobu jego konfiguracji. w każdym razie chodzi o to, że czasomierze nie są wystarczająco precyzyjne, aby liczyć na drugie wykonanie w ciągu kilku minut, w oparciu o wykonanie zdarzeń licznika czasu. –

0

Oto moje zdanie:

while ((DateTime.Now - lastRunTime).TotalSeconds < 600) 
    CurrentThread.Sleep(1000); 

lub po prostu zarejestrować czasomierza systemu Windows i wykonać w odpowiedzi na zdarzenie/zwrotna

public static void Main() 
{ 
    System.Timers.Timer aTimer = new System.Timers.Timer(); 
    aTimer.Elapsed+=new ElapsedEventHandler(OnTimedEvent); 
    // Set the Interval to 600 seconds. 
    aTimer.Interval=600000; 
    aTimer.Enabled=true; 

    Console.WriteLine("Press \'q\' to quit the sample."); 
    while(Console.Read()!='q'); 
} 

// Specify what you want to happen when the Elapsed event is raised. 
private static void OnTimedEvent(object source, ElapsedEventArgs e) 
{ 
    Console.WriteLine("10 minutes passed!"); 
} 
0

Zegary nie są dokładne, tylko przybliżone. Nie używaj logiki "po prostu dodaj 10 minut". Za każdym razem, gdy uruchamiasz timer, musisz sprawdzić, czy czas się nie kręci i dostosować.

np. Jeśli powiesz "Obudź mnie za 10 minut", a ona obudzi Cię w 10 minut i 1 sekundę, następny zegar musi być 9 minut i 59 sekund, a nie 10 minut.

Ponadto, chcesz przypisać następny zegar na końcu swojej logiki.

np. powiedzmy, że chcesz uruchomić taskA co 10 minut, a uruchomienie trwa 2 sekundy.Twój timer się uruchamia, a 10 minut później budzi się, aby uruchomić taskA. To się zaczyna, kończy, teraz dodajesz 10 minut. Ale wykonanie zadania zajęło 2 sekundy. Tak więc 10 minut od momentu uruchomienia kodu zostanie zniekształcone o 2 sekundy.

Co musisz zrobić, to przewidzieć, kiedy następnym razem będziesz biegać i znaleźć różnicę między teraz a potem i ustawić zegar na tę różnicę.

+0

Ale nie "dodaję" 10 minut. Im ustawiam licznik raz, mówiąc, żeby strzelał co 10 minut, a następnie uruchamiał mój kod w zdarzeniu "OnFired". Jeśli widzisz co mam na myśli. – Keith