2009-02-02 13 views
76

J'ai un service écrit en C# (.NET 1.1) et je veux qu'il effectue des actions de nettoyage à minuit tous les soirs. Je dois garder tout le code contenu dans le service, alors quel est le moyen le plus facile d'y parvenir? Utilisation de Thread.Sleep() et vérification du temps de roulement?Comment planifier un service Windows C# pour effectuer une tâche quotidiennement?

+8

Vous voulez vraiment faire un processus .NET qui se trouve dans la mémoire, toute la journée, et fait quelque chose à minuit? Pourquoi exactement, lors de l'installation de votre outil, ne pouvez-vous pas configurer un événement système qui se déclenche à minuit (ou à tout autre moment configurable) qui démarre votre application, fait son effet, puis disparaît? –

+5

Je sais que je serais un peu énervé si je découvrais que j'avais un service géré consommant 20 à 40 Mo de mémoire, courant tout le temps, qui ne fait rien sauf une fois par jour. :) –

+0

son dupliqué [http://stackoverflow.com/questions/503564/how-might-i-schedule-ac-sharp-windows-service-to-perform-a-task-daily) – csa

Répondre

83

Je ne voudrais pas utiliser Thread.Sleep(). Soit utiliser une tâche planifiée (comme d'autres l'ont mentionné), ou mettre en place une minuterie à l'intérieur de votre service, qui se déclenche périodiquement (toutes les 10 minutes par exemple) et vérifiez si la date a changé depuis la dernière:

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(); 
    } 
} 
+27

Ne serait-il pas plus logique de régler le minuteur pour qu'il expire à minuit, au lieu de se réveiller chaque minute pour vérifier l'heure? – Kibbee

+11

@Kibbee: peut-être que si le service ne s'exécute pas à minuit (pour une raison quelconque), vous pouvez exécuter la tâche dès que le service est à nouveau en cours d'exécution. – M4N

+4

Ensuite, vous devriez ajouter du code au démarrage du service pour savoir si vous devriez courir tout de suite, parce que le service ne fonctionnait pas, et il devrait l'être. Vous ne devriez pas vérifier toutes les 10 minutes en continu.Vérifiez si vous devez exécuter au démarrage, puis si vous ne comprenez pas la prochaine fois à exécuter. après cela, et régler une minuterie pour le temps que vous avez besoin. – Kibbee

3

La façon dont j'accomplis ceci est avec une minuterie.

Exécutez une minuterie serveur, demandez-lui de vérifier les heures/minutes toutes les 60 secondes.

Si c'est la bonne heure/minute, lancez votre processus. J'ai en fait ce résumé dans une classe de base que j'appelle OnceADayRunner. Permettez-moi de nettoyer un peu le code et je l'afficherai ici.

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(); 
     } 
    } 

Le boeuf de la méthode est dans le contrôle e.SignalTime.Minute/Hour.

Il y a des crochets là-dedans pour les tests, etc. mais c'est ce à quoi pourrait ressembler votre minuterie écoulée pour que tout fonctionne.

+0

Une légère Le retard dû à un serveur occupé peut lui faire manquer l'heure + minute. –

+1

True. Quand je l'ai fait, j'ai vérifié: Est-ce que le temps est maintenant supérieur à 00:00? Si oui, est-ce plus de 24 heures depuis la dernière fois que j'ai travaillé? Si oui, exécutez le travail, sinon attendez encore. – ZombieSheep

15

Est-ce que cela doit être un service réel? Pouvez-vous simplement utiliser les tâches planifiées intégrées dans le panneau de configuration de Windows.

+0

Étant donné que le service existe déjà, il peut être plus facile d'y ajouter la fonctionnalité. – M4N

+1

La conversion d'un service à usage restreint tel que celui-ci en application de console devrait être triviale. –

+7

Il a déjà dit, "Je dois garder tout le code contenu dans le service". Je ne pense pas qu'il soit nécessaire de remettre en question les exigences initiales. Cela dépend, mais parfois il y a de très bonnes raisons d'utiliser un service sur une tâche planifiée. – jeremcc

19

Une tâche quotidienne? On dirait que ça devrait juste être une tâche planifiée (panneau de contrôle) - pas besoin de service ici.

69

Découvrez Quartz.NET. Vous pouvez l'utiliser dans un service Windows. Il vous permet d'exécuter un travail basé sur un planning configuré, et il supporte même une simple syntaxe "cron job". J'ai eu beaucoup de succès avec ça.

Voici un exemple rapide de son utilisation:

// 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

Une bonne suggestion - dès que vous faites quelque chose, même un peu plus complexe que les choses de base de minuterie, vous devriez regarder Quartz. – serg10

+1

Quartz est si facile à utiliser, je dirais que même pour une tâche super simple, il suffit d'aller de l'avant et de l'utiliser. Cela donnera la possibilité d'ajuster facilement la programmation. –

+0

Super simple n'est pas vraiment? La plupart des débutants vont attraper ce problème ici http://stackoverflow.com/questions/24628372/quartz-job-scheduler-in-windows-service – batmaci

1

I suggère que vous utilisez une minuterie, mais réglez-le pour vérifier toutes les 45 secondes, pas minutes. Sinon, vous pouvez rencontrer des situations où, avec une lourde charge, la vérification d'une minute particulière est manquée, car entre le moment où la minuterie se déclenche et le moment où votre code s'exécute et vérifie l'heure actuelle, vous avez peut-être manqué la minute cible.

+0

Si vous ne vérifiez pas pour vous assurer qu'il n'a pas déjà été exécuté une fois, il est possible que vous atteigniez 00:00 deux fois si vous vérifiez pendant 45 secondes. –

+0

Bon point. Ce problème peut être évité en appelant Sleep (15000) à la fin de la procédure de vérification. (ou peut-être 16000 ms, juste pour être du bon côté ;-) – Treb

+0

Je suis d'accord, vous devriez vérifier si c'est déjà exécuté, quelle que soit la durée de minuterie choisie. – GWLlosa

2

Comme d'autres l'ont déjà écrit, une minuterie est la meilleure option dans le scénario que vous avez décrit.

En fonction de vos besoins exacts, il peut ne pas être nécessaire de vérifier l'heure actuelle chaque minute. Si vous n'avez pas besoin d'effectuer l'action exactement à minuit, mais seulement dans l'heure après minuit, vous pouvez opter pour Martin's approach uniquement en vérifiant si la date a changé. Si la raison pour laquelle vous voulez effectuer votre action à minuit est que vous attendez une faible charge de travail sur votre ordinateur, mieux vaut faire attention: la même hypothèse est souvent faite par d'autres, et soudainement, vous avez 100 actions de nettoyage entre 0. : 00 et 0:01

Dans ce cas, vous devriez commencer votre nettoyage à un moment différent. Je fais habituellement ces choses pas à l'heure d'horloge, mais à des heures et demie (1:30 étant ma préférence personnelle)

-2

Essayez ceci:

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 
    } 
} 
-1

Voici mon morceau de code qui fonctionne:

protected override void OnStart(string[] args) 
    { 
      timer = new Timer(); //Initialize the globally declared timer 
      //SETTING THE INTERVAL FROM CONFIG FILE (10 SECONDS) 
      timer.Interval = Convert.ToInt32(ConfigurationManager.AppSettings["TimerInterval"]); 
      timer.Enabled = true; 
      timer.Elapsed += timer_Elapsed;   
    } 

Dans votre événement écoulé, vous pouvez le faire:

private void timer_Elapsed(object sender, ElapsedEventArgs e) 
    { 
      timer.Enabled = false;   
      string ScheduleTime = GlobalVariables.ScheduledTime;//GETTING SCHEDULED TIME FROM CONFIG IN (HH:mm) FORMAT 
      string CurrentHourMin = DateTime.Now.ToString("HH:mm");//GETTING CURRENT TIME IN HH:mm format 
      if (ScheduleTime == CurrentHourMin) 
      { 
       // YOUR CODE 
      } 
      System.Threading.Thread.Sleep(60000); //Putting thread to sleep for 60 seconds to skip the current minute to avoid re-execution of code 
      timer.Enabled = true;     
    }