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

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

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

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

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

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

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

UPDATE:
Я добавляю свой алгоритм для вычисления временного интервала для таймера. Этот метод принимает параметр "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);
}

Ответы [ 6 ]

31 голосов
/ 21 ноября 2008

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

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

int minutes = DateTime.Now.Minute;
int adjust = 10 - (minutes % 10);
timer.Interval = adjust * 60 * 1000;  
11 голосов
/ 21 ноября 2008

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

4 голосов
/ 21 ноября 2008

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

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

    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);
        }            
    }
2 голосов
/ 04 мая 2009

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

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

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

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

1 голос
/ 21 ноября 2008

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

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

0 голосов
/ 26 сентября 2011

Моя цель - запускать импорт около 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();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...