Как я могу вызывать событие каждый час (или определенный интервал времени каждый час) в .NET?

StackOverflow https://stackoverflow.com/questions/307798

  •  08-07-2019
  •  | 
  •  

Вопрос

Я работаю над небольшим веб-сканером, который будет запускаться в системном трее и сканировать веб-сайт каждый час.

Как лучше всего заставить .NET вызывать событие каждый час или какой-либо другой интервал для выполнения какой-либо задачи.Например, я хочу запускать событие каждые 20 минут в зависимости от времени.Мероприятие будет организовано по адресу:

00:20
00:40
01:00
01:20
01:40

и так далее.Лучший способ, который я могу придумать, — это создать цикл в потоке, который постоянно проверяет, делится ли время на заданный интервал, и вызывает событие обратного вызова, если время достигнуто.Я чувствую, что должен быть лучший способ.

я бы использовал Timer но я бы предпочел что-то, что следует «расписанию», которое работает по часам, или что-то в этом роде.

Возможно ли это без настройки моего приложения в планировщике задач Windows?

ОБНОВЛЯТЬ:
Добавляю свой алгоритм расчета интервала времени для таймера.Этот метод принимает "minute", который указывает, в какое время таймер должен сработать.Например, если "minute" параметр равен 20, то таймер будет тикать с интервалами, указанными в приведенном выше расписании.

int CalculateTimerInterval(int minute)
{
    if (minute <= 0)
        minute = 60;
    DateTime now = DateTime.Now;

    DateTime future = now.AddMinutes((minute - (now.Minute % minute))).AddSeconds(now.Second * -1).AddMilliseconds(now.Millisecond * -1);

    TimeSpan interval = future - now;

    return (int)interval.TotalMilliseconds;
}

Этот код используется следующим образом:

static System.Windows.Forms.Timer t;
const int CHECK_INTERVAL = 20;


static void Main()
{
    t = new System.Windows.Forms.Timer();
    t.Interval = CalculateTimerInterval(CHECK_INTERVAL);
    t.Tick += new EventHandler(t_Tick);
    t.Start();
}

static void t_Tick(object sender, EventArgs e)
{
    t.Interval = CalculateTimerInterval(CHECK_INTERVAL);
}
Это было полезно?

Решение

System.Timers.Timer.Если вы хотите бегать в определенное время дня, вам нужно будет выяснить, сколько времени осталось до следующего раза, и установить это в качестве интервала.

Это только основная идея.В зависимости от того, насколько точными вам нужно быть, вы можете сделать больше.

int minutes = DateTime.Now.Minute;
int adjust = 10 - (minutes % 10);
timer.Interval = adjust * 60 * 1000;  

Другие советы

Вы можете найти помощь на Quartz.net http://quartznet.sourceforge.net/

Вот пример облегченной системы, использующей синхронизацию потоков и асинхронный вызов.

Я знаю, что есть некоторые недостатки, но мне нравится использовать это вместо таймера при запуске длительного процесса (например, запланированных серверных служб).Поскольку он выполняется встроенно в поток таймера, вам не нужно беспокоиться о том, что он снова запустится до завершения исходного вызова.Это можно было бы немного расширить, чтобы заставить его использовать массив дат и времени в качестве времени срабатывания или добавить к нему еще несколько возможностей.Я уверен, что некоторые из вас, ребята, знают способы получше.

    public Form1()
    {
        InitializeComponent();

        //some fake data, obviously you would have your own.
        DateTime someStart = DateTime.Now.AddMinutes(1);
        TimeSpan someInterval = TimeSpan.FromMinutes(2);

        //sample call
        StartTimer(someStart,someInterval,doSomething);
    }

    //just a fake function to call
    private bool doSomething()
    {
        DialogResult keepGoing = MessageBox.Show("Hey, I did something! Keep Going?","Something!",MessageBoxButtons.YesNo);
        return (keepGoing == DialogResult.Yes);
    }

    //The following is the actual guts.. and can be transplanted to an actual class.
    private delegate void voidFunc<P1,P2,P3>(P1 p1,P2 p2,P3 p3);
    public void StartTimer(DateTime startTime, TimeSpan interval, Func<bool> action)
    {
        voidFunc<DateTime,TimeSpan,Func<bool>> Timer = TimedThread;
        Timer.BeginInvoke(startTime,interval,action,null,null);
    }

    private void TimedThread(DateTime startTime, TimeSpan interval, Func<bool> action)
    {
        bool keepRunning = true;
        DateTime NextExecute = startTime;
        while(keepRunning)
        {
            if (DateTime.Now > NextExecute)
            {
                keepRunning = action.Invoke();
                NextExecute = NextExecute.Add(interval);
            }
            //could parameterize resolution.
            Thread.Sleep(1000);
        }            
    }

Другая стратегия заключается в том, чтобы записать ПОСЛЕДНИЙ ВРЕМЯ запуска процесса и определить, прошел ли с этого времени желаемый интервал.В этой стратегии вы должны закодировать свое событие так, чтобы оно сработало, если прошедшее время равно ИЛИ БОЛЬШЕ желаемого интервала.Таким образом, вы можете обрабатывать случаи, когда длинные интервалы (например, один раз в день) могут быть пропущены, если компьютер по какой-либо причине выключится.

Так, например:

  • LastRunDateTime = 02.05.2009, 20:00
  • Я хочу запускать процесс каждые 24 часа
  • В событии таймера проверьте, прошло ли 24 часа ИЛИ БОЛЬШЕ с момента последнего запуска процесса.
  • Если да, запустите процесс, обновите LastRunDateTime, добавив к нему желаемый интервал (в данном случае 24 часа, но все, что вам нужно)

Очевидно, что для восстановления после сбоя системы вам нужно будет сохранить LastRunDateTime где-нибудь в файле или базе данных, чтобы программа могла продолжить восстановление с того места, где она остановилась.

System.Windows.Forms.Timer (или System.Timers.Timer)

но поскольку теперь вы говорите, что не хотите использовать таймеры, вы можете запустить облегченный процесс ожидания в другом потоке (проверить время, поспать несколько секунд, снова проверить время...) или создать компонент, который вызывает событие (используя облегченный процесс ожидания) в определенное запланированное время или интервалы

Моя цель — запускать импорт около 03:00 каждую ночь.

Вот мой подход, используя System.Timers.Timer:

private Timer _timer;
private Int32 _hours = 0;
private Int32 _runAt = 3;

protected override void OnStart(string[] args)
{
    _hours = (24 - (DateTime.Now.Hour + 1)) + _runAt;
    _timer = new Timer();
    _timer.Interval = _hours * 60 * 60 * 1000;
    _timer.Elapsed += new ElapsedEventHandler(Tick);
    _timer.Start();
}

void Tick(object sender, ElapsedEventArgs e)
{
    if (_hours != 24)
    {
        _hours = 24;
        _timer.Interval = _hours * 60 * 60 * 1000;
    }

    RunImport();
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top