System.Threading.Timer в С#, похоже, не работает. Он работает очень быстро каждые 3 секунды
У меня есть объект таймера. Я хочу, чтобы он запускался каждую минуту. В частности, он должен запускать метод OnCallBack
и неактивен, пока работает метод OnCallBack
. Когда метод OnCallBack
завершается, он (a 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
Что я сделал не так?
Ответы
Ответ 1
Это неправильное использование System.Threading.Timer. Когда вы создаете экземпляр таймера, вы должны почти всегда делать следующее:
_timer = new Timer( Callback, null, TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );
Это даст указание таймеру указывать только один раз, когда интервал истек. Затем в вашей функции обратного вызова вы меняете таймер после завершения работы, а не раньше. Пример:
private void Callback( Object state )
{
// Long running operation
_timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );
}
Таким образом, нет необходимости в механизмах блокировки, потому что нет concurrency. Таймер активирует следующий обратный вызов после истечения следующего интервала + время длительной операции.
Если вам нужно запустить свой таймер ровно через N миллисекунд, я предлагаю вам измерить время длительной работы с помощью секундомера, а затем вызвать метод изменения соответствующим образом:
private void Callback( Object state )
{
Stopwatch watch = new Stopwatch();
watch.Start();
// Long running operation
_timer.Change( Math.Max( 0, TIME_INTERVAL_IN_MILLISECONDS - watch.ElapsedMilliseconds ), Timeout.Infinite );
}
Я сильно поощряет любого, кто делает .NET, и использует CLR, который не читал книгу Джеффри Рихтера - CLR через С#, чтобы прочитать это как можно скорее. Таймеры и пулы потоков объясняются в деталях там.
Ответ 2
Нет необходимости останавливать таймер, видеть хорошее решение из этого сообщения:
"Вы можете позволить таймеру продолжать активировать метод обратного вызова, но обернуть ваш не-реентеррантный код в Monitor.TryEnter/Exit. Нет необходимости останавливать/перезапускать таймер в этом случае, перекрывающиеся вызовы не будут получать блокировку и возврат немедленно".
private void CreatorLoop(object state)
{
if (Monitor.TryEnter(lockObject))
{
try
{
// Work here
}
finally
{
Monitor.Exit(lockObject);
}
}
}
Ответ 3
Используется ли System.Threading.Timer?
Если нет, System.Timers.Timer имеет удобные методы Start() и Stop() (и свойство AutoReset, вы можете установить значение false, чтобы Stop() не нужен, и вы просто вызываете Start() после выполнения).
Ответ 4
Я бы просто сделал:
private static Timer timer;
private static void Main()
{
timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds
Console.ReadLine();
}
private static void OnCallBack()
{
timer.Dispose();
Thread.Sleep(3000); //doing some long operation
timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds
}
И игнорируйте параметр period, так как вы пытаетесь самостоятельно управлять периодикой.
Ваш исходный код работает как можно быстрее, поскольку вы сохраняете указание 0
для параметра dueTime
. Из Timer.Change
:
Если dueTime равно нулю (0), метод обратного вызова вызывается немедленно.
Ответ 5
var span = TimeSpan.FromMinutes(2);
var t = Task.Factory.StartNew(async delegate / () =>
{
this.SomeAsync();
await Task.Delay(span, source.Token);
}, source.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
source.Cancel(true/or not);
// or use ThreadPool(whit defaul options thread) like this
Task.Start(()=>{...}), source.Token)
если u нравится использовать некоторый поток цикла внутри...
public async void RunForestRun(CancellationToken token)
{
var t = await Task.Factory.StartNew(async delegate
{
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(1), token)
.ContinueWith(task => { Console.WriteLine("End delay"); });
this.PrintConsole(1);
}
}, token) // drop thread options to default values;
}
// And somewhere there
source.Cancel();
//or
token.ThrowIfCancellationRequested(); // try/ catch block requred.