Mam obiekt zegarowy. Chcę, żeby było uruchamiane co minutę. W szczególności powinien uruchamiać OnCallBack
metodę i stać się nieaktywny, gdy OnCallBack
metoda jest uruchomiona. Po OnCallBack
zakończeniu metody (a OnCallBack
) restartuje licznik czasu.
Oto, co mam teraz:
private static Timer timer;
private static void Main()
{
timer = new Timer(_ => OnCallBack(), null, 0, 1000 * 10); //every 10 seconds
Console.ReadLine();
}
private static void OnCallBack()
{
timer.Change(Timeout.Infinite, Timeout.Infinite); //stops the timer
Thread.Sleep(3000); //doing some long operation
timer.Change(0, 1000 * 10); //restarts the timer
}
Jednak wydaje się, że nie działa. Działa bardzo szybko co 3 sekundy. Nawet jeśli podniesiesz kropkę (1000 * 10). Wygląda na to, że przymyka oko1000 * 10
Co zrobiłem źle?
Timer.Change
: "Jeśli dueTime ma wartość zero (0), metoda wywołania zwrotnego jest wywoływana natychmiast.". Wygląda na to, że to dla mnie zero.Odpowiedzi:
To nie jest prawidłowe użycie pliku System.Threading.Timer. Podczas tworzenia instancji Timera prawie zawsze należy wykonać następujące czynności:
To poinstruuje timer, aby tykał tylko raz po upływie interwału. Następnie w funkcji oddzwaniania zmieniasz licznik czasu po zakończeniu pracy, a nie wcześniej. Przykład:
Dlatego nie ma potrzeby stosowania mechanizmów blokujących, ponieważ nie ma współbieżności. Timer uruchomi następne wywołanie zwrotne po upływie następnego interwału + czas długotrwałej operacji.
Jeśli chcesz uruchomić stoper z dokładnością do N milisekund, proponuję zmierzyć czas długotrwałej operacji za pomocą Stopwatch, a następnie odpowiednio wywołać metodę Change:
I zdecydowanie zachęcić ktoś robi .NET i wykorzystuje CLR, który nie czytać książki Jeffreya Richtera - CLR via C # , aby przeczytać to jak najszybciej. Liczniki czasu i pule wątków są tam szczegółowo wyjaśnione.
źródło
private void Callback( Object state ) { // Long running operation _timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite ); }
.Callback
może zostać wywołana ponownie przed zakończeniem operacji.Long running operation
może to zająć znacznie więcej czasuTIME_INTERVAL_IN_MILLISECONDS
. Co by się wtedy stało?ThreadPool
, jeśli zdasz stoper? Myślę o scenariuszu, w którym nowy wątek jest odradzany, aby wykonać zadanie w określonych odstępach czasu - a po ukończeniu jest przenoszony do puli wątków.Nie ma potrzeby zatrzymywania timera, zobacz fajne rozwiązanie z tego postu :
„Możesz pozwolić zegarowi na kontynuowanie wywoływania metody wywołania zwrotnego, ale opakuj kod, który nie jest ponownie wprowadzany, w Monitor.TryEnter / Exit. W takim przypadku nie ma potrzeby zatrzymywania / restartowania licznika czasu; nakładające się wywołania nie uzyskają blokady i nie powrócą natychmiast.”
źródło
Czy używanie jest
System.Threading.Timer
obowiązkowe?Jeśli nie,
System.Timers.Timer
ma przydatneStart()
iStop()
metody (iAutoReset
właściwość, którą możesz ustawić na false, abyStop()
nie było potrzebne i po prostu wywołujeszStart()
po wykonaniu).źródło
Po prostu zrobiłbym:
I zignoruj parametr okresu, ponieważ próbujesz samodzielnie kontrolować okresy.
Oryginalny kod działa tak szybko, jak to możliwe, skoro trzymasz określając
0
dladueTime
parametru. OdTimer.Change
:źródło
Change()
metody?jeśli lubisz użyć jakiejś pętli w środku ...
źródło