Як я можу запланувати службу 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
Хіба не було б більше сенсу просто встановити таймер до півночі, замість того, щоб прокидатися щохвилини, щоб перевірити час?
Кібі

11
@Kibbee: можливо, якщо служба не працює опівночі (з будь-якої причини), можливо, ви захочете виконати завдання, як тільки служба знову запуститься.
M4N

4
Тоді вам слід додати трохи коду під час запуску служби, щоб з’ясувати, чи слід запускати вас відразу, оскільки служба не працювала, і вона повинна бути. Не слід постійно перевіряти кожні 10 хвилин. Перевірте, чи слід запускати під час запуску, а потім, якщо ні, не з’ясовуйте, коли наступного разу запускати. після цього та встановіть таймер на скільки часу вам потрібно.
Кіббі,

3
@Kibbee: так, я погоджуюсь (це незначна деталь, яку ми обговорюємо). Але навіть якщо таймер спрацьовує кожні 10 секунд, це не зашкодить (тобто це не трудомістко).
M4N

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
Хороша порада - як тільки ви зробите щось навіть трохи складніше, ніж основні речі з таймером, вам слід поглянути на кварц.
serg10

2
Кварц настільки простий у використанні, я б стверджував, що навіть для надпростого завдання просто використовуйте його. Це дасть можливість легко регулювати планування.
Andy White

Супер просто це не так? Більшість новачків вловлять цю проблему тут stackoverflow.com/questions/24628372/…
Еміль,

21

Щоденне завдання? Здається, це має бути просто заплановане завдання (панель управління) - тут немає потреби в послузі.


15

Це має бути фактична послуга? Чи можете ви просто використовувати вбудовані заплановані завдання на панелі керування Windows.


Оскільки послуга вже існує, можливо, буде простіше додати туди функціонал.
M4N 02

1
Перетворення вузької цільової служби, як-от на консольний додаток, має бути тривіальним.
Стівен Мартін

7
Він уже сказав: "Я повинен зберігати весь код, що міститься в службі". Я не вважаю за необхідне ставити під сумнів початкові вимоги. Це залежить, але іноді є дуже вагомі причини використовувати службу над запланованим завданням.
jeremcc 02

3
+1: Тому що вам завжди потрібно дивитись на свої проблеми з усіх боків.
GvS 02

6
Якби я справді мав виконувати лише одне завдання на день, то, мабуть, простий консольний запуск із запланованим завданням був би чудовим. Моя думка полягає в тому, що все залежить від ситуації, і сказати "Не користуватися службою Windows" - це не корисна відповідь IMO.
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.

Там є гачки для тестування тощо, але так може виглядати ваш минулий таймер, щоб все це працювало.


Невелика затримка через зайнятий сервер може спричинити пропуск години + хвилини.
Джон Б 02

1
Правда. Коли я це зробив, я перевірив: чи зараз час перевищує 00:00? Якщо так, чи минуло більше 24 годин з мого останнього виконання роботи? Якщо так, запустіть роботу, інакше почекайте ще раз.
ZombieSheep 02

2

Як вже писали інші, таймер - найкращий варіант у сценарії, який ви описали.

Залежно від ваших точних вимог, перевірка поточного часу щохвилини може не знадобитися. Якщо вам не потрібно виконувати дію рівно опівночі, а лише протягом години після опівночі, ви можете піти на підхід Мартіна лише перевірити, чи не змінилася дата.

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

У такому випадку вам слід розглянути можливість розпочати прибирання в інший час. Зазвичай я роблю це не за годину, а за півгодини (1.30 ранку - це моя особиста перевага)


1

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


Якщо ви не перевіряєте, щоб переконатися, що він ще не запущений жодного разу, існує ймовірність, що ви можете натиснути 00:00 двічі, якщо ви перевірите коли-небудь 45 секунд.
Майкл Медоус, 02

Гарна думка. Цю проблему можна уникнути, зателефонувавши до режиму сну (15000) наприкінці процедури перевірки. (або, можливо, 16000 мс, просто для того, щоб бути у безпеці ;-)
Треб, 02

Я згоден, вам слід перевірити, чи він уже запущений, незалежно від того, яку тривалість ви вибрали.
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.