Załóżmy, że mamy timer, który co jakiś próbuje połączyć się np. z usługą:
internal static class Sample { public static void Main() { using (Timer timer = new Timer(Run, null,0,1000)) { Thread.Sleep(500000); } } private static void Run(Object state) { Console.WriteLine("Operacja, ktora moze potrwac czasami nawet kilka minut."); } }
Powyższy kod w wielu sytuacjach jest poprawny. Należy jednak mieć na uwadze, że operacje takie jak połączenie z bazą danych czy usługą mogą potrwać bardzo długo. Z tego względu, może okazać się, że metoda Run będzie wywołana kilka razy w tym samym czasie. Łatwo przetestować taki scenariusz, dodając Thread.Sleep:
internal static class Sample { public static void Main() { using (Timer timer = new Timer(Run, null,0,1000)) { Thread.Sleep(500000); } } private static void Run(Object state) { Console.WriteLine("Przed"); Thread.Sleep(3000); Console.WriteLine("Po"); } }
W powyższym przypadku, Run i tak będzie wywoływany co każdą sekundę, skutkując wykonywaniem jednoczesnym kilku Run. Taki przypadek nie jest thread-safe i czasami po prostu zachowanie jest niepożądane – po co łączyć się dwa razy do bazy danych aby odświeżyć jakąś informacje? Jeśli jeden wątek nie może tego zrobić, to nie ma sensu wykonywanie dokładnie tego samego w drugim. Lepszym podejściem jest uruchomienie Run tylko raz i potem wywoływaniem w Run za każdym razem funkcji Change:
internal static class Sample { private static Timer _timer; public static void Main() { using (_timer = new Timer(Run, null, 1000, Timeout.Infinite)) { Thread.Sleep(500000); } } private static void Run(Object state) { Console.WriteLine("Przed"); Thread.Sleep(3000); Console.WriteLine("Po"); _timer.Change(1000, Timeout.Infinite); } }
Pierwsza różnica to utworzenie instancji Timer – zamiast parametru period, teraz ustawiamy dueTime, czyli opóźnienie z jakim ma zostać uruchomiony Timer. Następnie po każdym wykonaniu kodu w Run, wywołujemy Change ponownie z dueTime równym 1000.
Nie wiedziałem o tym. Dzieki
Przeczytałem kilka razy ten wpis i nie mogłem go w pełni zrozumieć. Skoro ‘period’ wynoszący 1000 powoduje odpalanie callbacka co 1000 ms, to 0 powinno oznaczać 0 ms – od razu. Wbrew pozorom tak nie jest 😉 Dopiero reflector wyjaśnił mi sytuację:
timer.m_period = (int) period == 0 ? uint.MaxValue : period;
Czyli 0 ustawia maksymalny, możliwy czas między powtórzeniami. Uważam, że lepiej jest napisać tak:
new Timer(Run, null, 1000, Timeout.Infinite)
Taki zapis mówi więcej osobie czytającej kod oraz pokrywa się z dokumentacją klasy Timer.
@Mad:
Zgadzam się. Duzo lepiej przekazac Timeout.Infinite. Poprawiam…
Jeśli komuś nie chce się sprawdzać w VS, to Timeout.Infinite = -1.
Btw, dodatkowo:
_timer.Change(1000, Timeout.Infinite);