Как я могу запланировать ежедневное выполнение задачи службой Windows C #?


84

У меня есть служба, написанная на C # (.NET 1.1), и я хочу, чтобы она выполняла некоторые действия по очистке в полночь каждую ночь. Я должен сохранить весь код, содержащийся в службе, так как же проще всего этого добиться? Использование Thread.Sleep()и проверка продления времени?


10
Вы действительно хотите создать .NET-процесс, который весь день сидит в памяти и что-то делает в полночь? Почему именно вы не можете после установки инструмента настроить системное событие, которое запускается в полночь (или в другое настраиваемое время), которое запускает ваше приложение, делает свое дело, а затем исчезает?
Роберт П.

6
Я знаю, что был бы немного зол, если бы узнал, что у меня есть управляемая служба, потребляющая 20-40 мегабайт памяти, работающая все время и ничего не делающая, кроме одного раза в день. :)
Роберт П.

его дублированная ссылка
csa

Ответы:


86

Я бы не стал использовать Thread.Sleep (). Либо используйте запланированную задачу (как упоминали другие), либо настройте таймер внутри своей службы, который периодически срабатывает (например, каждые 10 минут), и проверьте, изменилась ли дата с момента последнего запуска:

private Timer _timer;
private DateTime _lastRun = DateTime.Now.AddDays(-1);

protected override void OnStart(string[] args)
{
    _timer = new Timer(10 * 60 * 1000); // every 10 minutes
    _timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
    _timer.Start();
    //...
}


private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    // ignore the time, just compare the date
    if (_lastRun.Date < DateTime.Now.Date)
    {
        // stop the timer while we are running the cleanup task
        _timer.Stop();
        //
        // do cleanup stuff
        //
        _lastRun = DateTime.Now;
        _timer.Start();
    }
}

28
Разве не было бы разумнее просто установить таймер, чтобы истечь в полночь, вместо того, чтобы просыпаться каждую минуту, чтобы проверить время?
Кибби, 02

11
@Kibbee: возможно, если служба не работает в полночь (по какой-либо причине), вы можете захотеть выполнить задачу, как только служба снова запустится.
M4N 01

4
Затем вы должны добавить код при запуске службы, чтобы выяснить, следует ли вам сразу же запускать, потому что служба не работает, а должна быть. Не следует проверять каждые 10 минут непрерывно. Проверьте, следует ли запускать при запуске, а затем, если нет, определите, когда запускать следующий раз. после этого и установите таймер на необходимое вам время.
Kibbee 01

3
@Kibbee: да, я согласен (это мелочь, которую мы обсуждаем). Но даже если таймер срабатывает каждые 10 секунд, это не причинит никакого вреда (т.е. не займет много времени).
M4N 01

1
хорошее решение, но код выше отсутствует _timer.start () и _lastRun должны быть установлены на вчера, например: private DateTime _lastRun = DateTime.Now.AddDays (-1);
Zulu Z

72

Проверьте Quartz.NET . Вы можете использовать его в службе Windows. Он позволяет запускать задание на основе настроенного расписания и даже поддерживает простой синтаксис «cron job». Я добился большого успеха с этим.

Вот краткий пример его использования:

// Instantiate the Quartz.NET scheduler
var schedulerFactory = new StdSchedulerFactory();
var scheduler = schedulerFactory.GetScheduler();

// Instantiate the JobDetail object passing in the type of your
// custom job class. Your class merely needs to implement a simple
// interface with a single method called "Execute".
var job = new JobDetail("job1", "group1", typeof(MyJobClass));

// Instantiate a trigger using the basic cron syntax.
// This tells it to run at 1AM every Monday - Friday.
var trigger = new CronTrigger(
    "trigger1", "group1", "job1", "group1", "0 0 1 ? * MON-FRI");

// Add the job to the scheduler
scheduler.AddJob(job, true);
scheduler.ScheduleJob(trigger);

2
Хорошее предложение - как только вы сделаете что-то даже немного более сложное, чем базовый таймер, вы должны взглянуть на Quartz.
serg10 02

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

Супер просто не правда? Большинство новичков поймают эту проблему здесь stackoverflow.com/questions/24628372/…
Эмиль

21

Ежедневная задача? Похоже, это должно быть просто запланированное задание (панель управления) - здесь нет необходимости в обслуживании.


15

Это должна быть настоящая услуга? Можете ли вы просто использовать встроенные запланированные задачи в панели управления Windows.


Поскольку служба уже существует, возможно, будет проще добавить туда функциональность.
M4N 02

1
Преобразование такой узконаправленной службы в консольное приложение должно быть тривиальным.
Стивен Мартин

7
Он уже сказал: «Я должен сохранить весь код, содержащийся в службе». Я не считаю необходимым подвергать сомнению первоначальные требования. Это зависит от обстоятельств, но иногда есть очень веские причины использовать службу вместо запланированной задачи.
jeremcc 02

3
+1: Потому что всегда нужно смотреть на свои проблемы со всех сторон.
GvS 02

6
Если бы мне действительно приходилось выполнять только одну задачу в день, тогда, я думаю, было бы хорошо запустить простое консольное приложение с запланированной задачей. Я хочу сказать, что все зависит от ситуации, и сказать «Не используйте службу Windows» не является полезным ответом, ИМО.
jeremcc 02

3

Я делаю это с помощью таймера.

Запустите серверный таймер, пусть он проверяет часы / минуты каждые 60 секунд.

Если это правильный час / минута, запустите процесс.

На самом деле у меня это абстрагировано в базовый класс, который я называю OnceADayRunner.

Позвольте мне немного поправить код, и я выложу его здесь.

    private void OnceADayRunnerTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        using (NDC.Push(GetType().Name))
        {
            try
            {
                log.DebugFormat("Checking if it's time to process at: {0}", e.SignalTime);
                log.DebugFormat("IsTestMode: {0}", IsTestMode);

                if ((e.SignalTime.Minute == MinuteToCheck && e.SignalTime.Hour == HourToCheck) || IsTestMode)
                {
                    log.InfoFormat("Processing at: Hour = {0} - Minute = {1}", e.SignalTime.Hour, e.SignalTime.Minute);
                    OnceADayTimer.Enabled = false;
                    OnceADayMethod();
                    OnceADayTimer.Enabled = true;

                    IsTestMode = false;
                }
                else
                {
                    log.DebugFormat("Not correct time at: Hour = {0} - Minute = {1}", e.SignalTime.Hour, e.SignalTime.Minute);
                }
            }
            catch (Exception ex)
            {
                OnceADayTimer.Enabled = true;
                log.Error(ex.ToString());
            }

            OnceADayTimer.Start();
        }
    }

Суть метода заключается в проверке e.SignalTime.Minute / Hour.

Там есть крючки для тестирования и т. Д., Но так может выглядеть ваш истекший таймер, чтобы все это работало.


Небольшая задержка из-за загруженного сервера может привести к тому, что он пропустит час + минуту.
Jon B

1
Правда. Когда я это сделал, я проверил: время сейчас больше 00:00? Если да, то прошло ли больше 24 часов с тех пор, как я последний раз выполнял задание? Если да, запустите задание, в противном случае подождите снова.
ZombieSheep,

2

Как уже писали другие, таймер - лучший вариант в описанном вами сценарии.

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

Если причина, по которой вы хотите выполнить действие в полночь, заключается в том, что вы ожидаете низкой нагрузки на свой компьютер, лучше позаботьтесь: такое же предположение часто делают другие, и внезапно у вас есть 100 действий по очистке, начинающихся между 0:00 и 0 : 01 утра

В этом случае вам следует подумать о том, чтобы начать очистку в другое время. Я обычно делаю это не в час, а в полчаса (я предпочитаю 1.30).


1

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


Если вы не проверяете, что он еще не запускался один раз, есть вероятность, что вы можете дважды достичь 00:00, если вы проверяете каждые 45 секунд.
Майкл Медоуз,

Хорошая точка зрения. Этой проблемы можно избежать, вызвав Sleep (15000) в конце процедуры проверки. (или, возможно, 16000 мс, на всякий случай ;-)
Треб

Я согласен, вы должны проверить, запущен ли он уже, независимо от того, какую продолжительность таймера вы выбрали.
GWLlosa 02


0

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


-2

Попробуй это:

public partial class Service : ServiceBase
{
    private Timer timer;
    public Service()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
        SetTimer();
    }

    private void SetTimer()
    {
        if (timer == null)
        {
            timer = new Timer();
            timer.AutoReset = true;
            timer.Interval = 60000 * Convert.ToDouble(ConfigurationManager.AppSettings["IntervalMinutes"]);
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            timer.Start();
        }
    }

    private void timer_Elapsed(object source, System.Timers.ElapsedEventArgs e)
    {
        //Do some thing logic here
    }

    protected override void OnStop()
    {
        // disposed all service objects
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.