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?
risposta
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();
}
}
Non avrebbe più senso impostare semplicemente il timer alla scadenza a mezzanotte, invece di svegliarti ogni minuto per controllare l'ora? – Kibbee
@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
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
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.
Un leggero il ritardo dovuto a un server occupato potrebbe far perdere l'ora + minuto. –
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
Deve essere un servizio effettivo? Puoi semplicemente usare le attività programmate nel pannello di controllo di Windows.
Poiché il servizio esiste già, potrebbe essere più semplice aggiungervi la funzionalità. – M4N
La conversione di un servizio a scopo limitato come questo in un'app per console dovrebbe essere banale. –
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
Un compito giornaliero? Sembra che dovrebbe essere solo un compito programmato (pannello di controllo) - non c'è bisogno di un servizio qui.
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);
Un buon suggerimento: non appena fai qualcosa di leggermente più complesso delle cose base del Timer, dovresti guardare Quartz. – serg10
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. –
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
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.
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. –
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
Sono d'accordo, dovresti controllare se è già in esecuzione, indipendentemente da quale durata del timer scegli. – GWLlosa
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)
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
}
}
Si può anche provare il TaskSchedulerLibrary qui http://visualstudiogallery.msdn.microsoft.com/a4a4f042-ffd3-42f2-a689-290ec13011f8
Implementare la classe astratta AbstractScheduledTask
e chiamare il metodo statico ScheduleUtilityFactory.AddScheduleTaskToBatch
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;
}
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? –
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. :) –
il suo duplicato [collegamento] (http://stackoverflow.com/questions/503564/how-might-i-schedule-ac-sharp-windows-service-to-perform-a-task-daily) – csa