Таймер запускается каждые 5 минут
Как я могу запускать некоторую функцию каждые 5 минут? Пример: я хочу запустить sendRequest()
только во время 14:00, 14:05, 14:10 и т.д.
Я хотел бы сделать это программно, на С#. Приложение является службой Windows.
Ответы
Ответ 1
Ответ, опубликованный шесть лет назад, полезен. Тем не менее, IMHO с современным С# теперь лучше использовать API Task
с async
и await
. Кроме того, я немного отличаюсь от особенностей реализации, например, как управлять вычислением задержки и как округлить текущее время до следующего пятиминутного интервала.
Сначала предположим, что метод sendRequest()
возвращает void
и не имеет параметров. Тогда предположим, что основное требование - запускать его примерно каждые пять минут (т.е. Не так важно, чтобы он выполнялся точно на пятиминутных делениях часа). Тогда это можно реализовать очень легко, например:
async Task RunPeriodically(Action action, TimeSpan interval, CancellationToken token)
{
while (true)
{
action();
await Task.Delay(interval, token);
}
}
Его можно вызвать так:
CancellationTokenSource tokenSource = new CancellationTokenSource();
Task timerTask = RunPeriodically(sendRequest, TimeSpan.FromMinutes(5), tokenSource.Token);
Когда вызывается tokenSource.Cancel()
, цикл будет прерван TaskCanceledException
, созданным в инструкции await Task.Delay(...)
. В противном случае метод sendRequest()
будет вызываться каждые пять минут (при этом он вызывается немедленно, когда вызывается метод RunPeriodically()
& hellip; вы можете изменить порядок операторов в цикле, если вы хотите, чтобы он тоже был в первый раз).
Это простейшая версия. Если вместо этого вы хотите выполнить действие точно с пятиминутными интервалами, вы можете сделать что-то подобное, но вычислите следующее время выполнения и задержку на соответствующее количество времени. Например:
// private field somewhere appropriate; it would probably be best to put
// this logic into a reusable class.
DateTime _nextRunTime;
async Task RunPeriodically(Action action,
DateTime startTime, TimeSpan interval, CancellationToken token)
{
_nextRunTime = startTime;
while (true)
{
TimeSpan delay = _nextRunTime - DateTime.UtcNow;
if (delay > TimeSpan.Zero)
{
await Task.Delay(delay, token);
}
action();
_nextRunTime += interval;
}
}
Вызывается следующим образом:
CancellationTokenSource tokenSource = new CancellationTokenSource();
DateTime startTime = RoundCurrentToNextFiveMinutes();
Task timerTask = RunPeriodically(sendRequest,
startTime, TimeSpan.FromMinutes(5), tokenSource.Token);
Где вспомогательный метод RoundCurrentToNextFiveMinutes()
выглядит следующим образом:
DateTime RoundCurrentToNextFiveMinutes()
{
DateTime now = DateTime.UtcNow,
result = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0);
return result.AddMinutes(((now.Minute / 5) + 1) * 5);
}
В любом примере объект timerTask
может использоваться для контроля состояния цикла. В большинстве случаев это, вероятно, не нужно. Но если вы хотите иметь возможность, например, await
, пока какая-либо другая часть кода не отменяет цикл, этот объект будет использоваться вами.
Обратите внимание, что метод Task.Delay()
использует таймер в своей реализации. Вышеупомянутое не предлагается с целью избежать использования таймера, а скорее для реализации реализации исходной цели с использованием современных функций С# async
/await
. Эта версия будет более чистой с другим кодом, который уже использует async
/await
.
Ответ 2
Используйте System.Threading.Timer
. Вы можете периодически указывать метод для вызова.
Пример:
Timer timer = new Timer(Callback, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
public void Callback(object state) {
Console.WriteLine("The current time is {0}", DateTime.Now);
}
Вы можете использовать второй параметр для передачи состояния в обратный вызов.
Обратите внимание, что вам нужно как-то поддерживать ваше приложение (например, запускать его как службу).
Как убедиться, что он работает в hh:mm
, где mm % 5 == 0
, вы можете сделать следующее.
DateTime now = DateTime.Now;
int additionalMinutes = 5 - now.Minute % 5;
if(additionalMinutes == 0) {
additionalMinutes = 5;
}
var nearestOnFiveMinutes = new DateTime(
now.Year,
now.Month,
now.Day,
now.Hour,
now.Minute,
0
).AddMinutes(additionalMinutes);
TimeSpan timeToStart = nearestOnFiveMinutes.Subtract(now);
TimeSpan tolerance = TimeSpan.FromSeconds(1);
if (timeToStart < tolerance) {
timeToStart = TimeSpan.Zero;
}
var Timer = new Timer(callback, null, timeToStart, TimeSpan.FromMinutes(5));
Обратите внимание, что tolerance
необходимо в случае, если этот код выполняется, когда now
очень близко к ближайшему hh:mm
с mm % 5 == 0
. Вероятно, вы можете уйти со значением меньше одной секунды, но я оставлю это вам.
Ответ 3
Использовать System.Threading.Timer:
var timer = new Timer(TimerTick, null, TimeSpan.Zero, new TimeSpan(0, 0, 0, 1));
int lastMinute = 1;
void TimerTick(object state)
{
var minute = DateTime.Now.Minutes;
if (minute != lastMinute && minute % 5 == 0)
{
lastMinute = minute;
//do stuff
}
}
Это может выглядеть несколько неуклюже и неэффективно, так как он должен вызывать каждую секунду, но фактическое количество циклов ЦП, которые привыкнут к проверке один раз в секунду, совершенно ничтожно мало на любом оборудовании.
(Извините, если код не на 100% правильный, у меня сейчас нет IDE.
Ответ 4
Вы можете создать поток, который включает в себя такой цикл
void Runner(){
while(1){
Thread t = new thread( target_method );
t.start();
sleep(5 * 60 * 1000);
}
}
Это немного быстро и грязно, но я буду делать это:)
Ответ 5
у вас может быть поток, запускающий этот первый
- проверяет, как долго спать до следующей 5-й минуты (так что если он 13:59:51, он спит в течение 9 секунд).
- выполняет действие;
- затем спит в течение 5 минут
- переходит к шагу 2
Ответ 6
Этот класс вам нужен, вы просто настраиваете время между вызовами делегата и вашим делегатом, и вот оно:
http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx
Ответ 7
Лучше дать вам собственный поток, который выполняет метод условие
While(programStatus == ProgramStatus.Working)
{
While (true)
{
// Do your stuff
}
Thread.Sleep(TheTimeYouWantTheThreadToWaitTillTheNextOperation);
}
Ответ 8
То, что вы ищете, это код, который реализует задания cron. Есть несколько библиотек, которые делают это (quartz.net - один из них). Я обнаружил, что многие из них громоздки и имеют множество функций. Вот более реалистичная реализация, которую я использовал в некоторых проектах:
http://blog.bobcravens.com/2009/10/an-event-based-cron-scheduled-job-in-c/
P.S. Сейчас мой сервер отключен. Я работаю над этим.
Надеюсь, что это поможет.
Боб
Ответ 9
Есть несколько способов сделать это, вы можете создать таймер, который запускается каждые 5 минут и запускать его, когда время достигает одного из 5-минутных интервалов или запускается каждые несколько секунд и проверяет, является ли время делимым на 5
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); // create a new timer
timer.interval = 300000; //300000 = 5 minutes
затем создайте функцию галочки и добавьте обработчик событий
timer.Tick += new EventHandler(TimerTickHandler); //add the event handler
timer.Start(); //start the timer
Ответ 10
http://www.csharphelp.com/2006/02/c-timer-demo/
http://dotnetperls.com/timer
Несколько учебников о времени в С#
Ответ 11
Я согласен с Питером Дунихо - современный С# намного лучше в этом.
Я бы предложил использовать Microsoft Reactive Framework (NuGet "System.Reactive" ), а затем вы можете сделать это:
IObservable<long> timer =
Observable
.Defer(() =>
{
var now = DateTimeOffset.Now;
var result = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, 0, 0, now.Offset);
result = result.AddMinutes(((now.Minute / 5) + 1) * 5);
return Observable.Timer(result, TimeSpan.FromMinutes(5.0));
});
timer.Subscribe(x =>
{
/* Your code here that run on the 5 minute every 5 minutes. */
});
Я получаю такой результат:
2016/08/11 14:40:00 +09:30
2016/08/11 14:45:00 +09:30
2016/08/11 14:50:00 +09:30