У меня есть объект таймера. Я хочу, чтобы он запускался каждую минуту. В частности, он должен запускать OnCallBack
метод и становиться неактивным во время выполнения OnCallBack
метода. После OnCallBack
завершения метода он (а OnCallBack
) перезапускает таймер.
Вот что у меня есть прямо сейчас:
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
}
Однако, похоже, это не работает. Он работает очень быстро каждые 3 секунды. Даже если поднять точку (1000 * 10). Вроде закрывает глаза на1000 * 10
Что я сделал не так?
Timer.Change
: «Если dueTime равен нулю (0), метод обратного вызова вызывается немедленно.». Похоже, для меня это ноль.Ответы:
Это неправильное использование System.Threading.Timer. Когда вы создаете экземпляр таймера, вы почти всегда должны делать следующее:
Это укажет таймеру сработать только один раз по истечении интервала. Затем в своей функции обратного вызова вы меняете таймер после завершения работы, а не раньше. Пример:
Таким образом, нет необходимости в механизмах блокировки, потому что нет параллелизма. Таймер запустит следующий обратный вызов по истечении следующего интервала + время длительной операции.
Если вам нужно запустить таймер с точностью до N миллисекунд, я предлагаю вам измерить время длительной операции с помощью секундомера, а затем соответствующим образом вызвать метод Change:
Я настоятельно рекомендую всем, кто занимается .NET и использует CLR, кто не читал книгу Джеффри Рихтера - CLR через C # , прочитать как можно скорее. Там очень подробно описаны таймеры и пулы потоков.
источник
private void Callback( Object state ) { // Long running operation _timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite ); }
.Callback
может быть вызван снова до завершения операции.Long running operation
могло занять гораздо больше времениTIME_INTERVAL_IN_MILLISECONDS
. Что тогда будет?ThreadPool
, если вы передадите таймер? Я думаю о сценарии, в котором новый поток создается для выполнения задания с определенным интервалом, а затем передается в пул потоков по завершении.Необязательно останавливать таймер, см. Хорошее решение из этого сообщения :
«Вы можете позволить таймеру продолжить запуск метода обратного вызова, но обернуть свой нереентерабельный код в Monitor.TryEnter / Exit. В этом случае нет необходимости останавливать / перезапускать таймер; перекрывающиеся вызовы не будут получать блокировку и немедленно возвращаться».
источник
Использует
System.Threading.Timer
обязательно?Если нет, то
System.Timers.Timer
есть под рукойStart()
иStop()
методы (иAutoReset
свойство можно установить в FALSE, так чтоStop()
не требуется , и вы просто звонитеStart()
после выполнения).источник
Я бы просто сделал:
И игнорируйте параметр периода, поскольку вы пытаетесь самостоятельно управлять периодичностью.
Ваш исходный код работает так же быстро , как это возможно, так как вы сохраняете указания
0
дляdueTime
параметра. ОткудаTimer.Change
:источник
Change()
метод?если вам нравится использовать какой-то цикл внутри ...
источник