Таймер запускается каждые 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

Ответ 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

Ответ 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