2009-02-02 8 views
76

Ho un servizio scritto in C# (.NET 1.1) e voglio che esegua alcune azioni di pulizia a mezzanotte ogni notte. Devo mantenere tutto il codice contenuto nel servizio, quindi qual è il modo più semplice per farlo? L'uso di Thread.Sleep() e il controllo del tempo che scorre?Come posso programmare un servizio Windows C# per eseguire un'attività quotidianamente?

+8

Sei sicuro di voler fare un processo .NET che si trova in memoria, per tutto il giorno, e fa qualcosa a mezzanotte? Perché proprio non riesci, dopo aver installato il tuo strumento, a configurare un evento di sistema che spara a mezzanotte (o qualche altra volta configurabile) che avvia la tua app, fa la sua parte, quindi se ne va? –

+5

So che sarei un po 'incazzato se scoprissi di avere un servizio gestito che consuma 20-40 mega di memoria, sempre in esecuzione, che non fa nulla se non una volta al giorno. :) –

+0

il suo duplicato [collegamento] (http://stackoverflow.com/questions/503564/how-might-i-schedule-ac-sharp-windows-service-to-perform-a-task-daily) – csa

risposta

83

Non vorrei usare Thread.Sleep(). O utilizzare un'operazione pianificata (come altri hanno detto), oppure impostare un timer all'interno del vostro servizio, che spara periodicamente (ogni 10 minuti, per esempio) e verificare se la data è cambiato dall'ultima esecuzione:

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

Non avrebbe più senso impostare semplicemente il timer alla scadenza a mezzanotte, invece di svegliarti ogni minuto per controllare l'ora? – Kibbee

+11

@Kibbee: forse se il servizio non è in esecuzione a mezzanotte (per qualsiasi motivo), è possibile che si desideri eseguire l'attività non appena il servizio è di nuovo in esecuzione. – M4N

+4

Quindi dovresti aggiungere del codice all'avvio del servizio per capire se dovresti eseguire subito, perché il servizio non era in esecuzione, e dovrebbe essere. Non dovresti controllare continuamente ogni 10 minuti.Controlla se è necessario eseguire all'avvio e, in caso contrario, la prossima volta che esegui. dopodiché, imposta un timer per il tempo necessario. – Kibbee

3

Il modo in cui lo realizzo è con un timer.

Eseguire un timer del server, fare controllare l'ora/minuto ogni 60 secondi.

Se è l'ora/minuto giusto, quindi eseguire il processo.

In realtà ho questo estratto in una classe base che chiamo OnceADayRunner.

Lasciami pulire un po 'il codice e lo posterò qui.

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

La carne del metodo è nel controllo/ora e.SignalTime.Minute.

Ci sono dei ganci per testare, ecc. Ma questo è il modo in cui il vostro timer potrebbe apparire per far funzionare tutto.

+0

Un leggero il ritardo dovuto a un server occupato potrebbe far perdere l'ora + minuto. –

+1

Vero. Quando l'ho fatto, ho controllato: l'ora è maggiore delle 00:00? Se è così, sono più di 24 ore dall'ultima volta che ho svolto il lavoro? In tal caso, esegui il lavoro, altrimenti attendi di nuovo. – ZombieSheep

15

Deve essere un servizio effettivo? Puoi semplicemente usare le attività programmate nel pannello di controllo di Windows.

+0

Poiché il servizio esiste già, potrebbe essere più semplice aggiungervi la funzionalità. – M4N

+1

La conversione di un servizio a scopo limitato come questo in un'app per console dovrebbe essere banale. –

+7

Ha già detto: "Devo mantenere tutto il codice contenuto nel servizio". Non ritengo sia necessario mettere in discussione i requisiti originali. Dipende, ma a volte ci sono ottimi motivi per utilizzare un servizio su un'attività pianificata. – jeremcc

19

Un compito giornaliero? Sembra che dovrebbe essere solo un compito programmato (pannello di controllo) - non c'è bisogno di un servizio qui.

69

Check out Quartz.NET. Puoi usarlo all'interno di un servizio di Windows. Consente di eseguire un lavoro in base a una pianificazione configurata e supporta anche una semplice sintassi "cron job". Ho avuto molto successo con questo.

Ecco un rapido esempio del suo utilizzo:

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

Un buon suggerimento: non appena fai qualcosa di leggermente più complesso delle cose base del Timer, dovresti guardare Quartz. – serg10

+1

Quartz è così facile da usare, direi che anche per un compito semplicissimo, basta andare avanti e usarlo. Darà la possibilità di adattare facilmente la programmazione. –

+0

Super semplice non è davvero? La maggior parte dei neofiti prenderà questo problema qui http://stackoverflow.com/questions/24628372/quartz-job-scheduler-in-windows-service – batmaci

1

I suggerirebbe di utilizzare un timer, ma impostarlo per controllare ogni 45 secondi, non i minuti. In caso contrario, è possibile imbattersi in situazioni in cui con carichi pesanti, il controllo per un minuto particolare viene perso, perché tra il momento in cui il timer si attiva e il tempo in cui il codice viene eseguito e controlla l'ora corrente, potrebbe essere mancato il minuto target.

+0

Se non stai verificando che non sia già stato eseguito una volta, c'è il rischio che tu possa colpire 00:00 due volte se controlli 45 secondi. –

+0

Buon punto. Questo problema potrebbe essere evitato chiamando Sleep (15000) alla fine della procedura di controllo. (o forse 16000 ms, giusto per essere al sicuro ;-) – Treb

+0

Sono d'accordo, dovresti controllare se è già in esecuzione, indipendentemente da quale durata del timer scegli. – GWLlosa

2

Come altri già hanno scritto, un timer è l'opzione migliore nello scenario che hai descritto.

In base ai requisiti esatti, potrebbe non essere necessario controllare l'ora corrente ogni minuto. Se non è necessario eseguire l'azione esattamente a a mezzanotte, ma solo entro un'ora dopo la mezzanotte, è possibile utilizzare il Martin's approach per verificare solo se la data è stata modificata.

Se il motivo si desidera eseguire la vostra azione a mezzanotte è che ci si aspetta un carico di lavoro basso sul vostro computer, meglio fare attenzione: La stessa ipotesi è spesso fatta da altri, e improvvisamente si avere 100 azioni di pulizia dando il via tra 0 : 00 e 0:01 am

In tal caso, è consigliabile prendere in considerazione l'avvio della pulizia in un altro momento. Di solito faccio le cose per niente ora d'orologio, ma a ore e mezza (01:30 essendo la mia preferenza personale)

-2

Prova questo:

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

Ecco il mio pezzo di codice che funziona:

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

Nel vostro evento trascorso, si può fare questo:

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;     
    } 
Problemi correlati