2010-07-14 10 views
57

Ho cercato su SO e ho trovato risposte su Quartz.net. Ma sembra essere troppo grande per il mio progetto. Voglio una soluzione equivalente, ma più semplice e (nella migliore delle ipotesi) in-code (non è necessaria alcuna libreria esterna). Come posso chiamare un metodo ogni giorno, in un momento specifico?Come chiamare un metodo ogni giorno, in un momento specifico, in C#?

Ho bisogno di aggiungere alcune informazioni su questo:

  • il modo più semplice (e brutto) per fare questo, è controllare il tempo ogni secondo/minuto e chiamare il metodo, al momento giusto

Voglio un modo più efficace per farlo, non c'è bisogno di controllare costantemente il tempo, e ho il controllo sul fatto che il lavoro sia fatto o meno. Se il metodo non riesce (a causa di eventuali problemi), il programma dovrebbe sapere di scrivere per registrare/inviare una e-mail. Ecco perché ho bisogno di chiamare un metodo, non programmare un lavoro.

Ho trovato questa soluzione Call a method at fixed time in Java in Java. C'è un modo simile in C#?

EDIT: Ho fatto questo. Ho aggiunto un parametro a Main() e creato un blocco (pianificato dall'Utilità di pianificazione di Windows) per eseguire il programma con questo parametro. Il programma viene eseguito, esegue il lavoro e quindi esce. Se un lavoro fallisce, è in grado di scrivere log e inviare e-mail. Questo approccio si adatta alle mie esigenze ben :)

+2

quello legato questione sembra indicare che un metodo all'interno della vostra applicazione in esecuzione deve essere chiamato periodicamente. È questo il caso? Avrà un impatto se è necessaria la pianificazione in-process o se è possibile utilizzare semplicemente lo scheduler di Windows. – paxdiablo

+0

il mio programma, come da requisiti, funzionerà continuamente – Vimvq1987

+0

Ehi, non posso credere che tu abbia definito la mia risposta "brutta". Loro combattono le parole :-) – paxdiablo

risposta

67
  • Creare una console app che fa quello che stai cercando
  • Utilizzare Windows "Scheduled Tasks" funzionalità per avere quel console app eseguito al tempo necessario per eseguire

Questo è tutto ciò che serve!

Update: se si vuole fare questo all'interno della vostra applicazione, sono disponibili diverse opzioni:

  • in un Windows Form app, è possibile attingere l'evento Application.Idle e controllare per vedere se si Ho raggiunto il momento della giornata per chiamare il tuo metodo. Questo metodo viene chiamato solo quando la tua app non è impegnata con altre cose. Un rapido controllo per vedere se il tuo tempo target è stato raggiunto non dovrebbe mettere troppo stress sulla tua app, penso ...
  • in un'applicazione web ASP.NET, ci sono metodi per "simulare" l'invio di eventi programmati - controllare questo CodeProject article
  • e, naturalmente, si può anche solo semplicemente "roll your own" in qualsiasi applicazione .NET - controlla questo CodeProject article per un esempio di implementazione

Update # 2: se si vuoi controllare ogni 60 minuti, potresti creare un timer che si riattiva ogni 60 minuti e se il tempo è scaduto, chiama il metodo.

Qualcosa di simile a questo:

using System.Timers; 

const double interval60Minutes = 60 * 60 * 1000; // milliseconds to one hour 

Timer checkForTime = new Timer(interval60Minutes); 
checkForTime.Elapsed += new ElapsedEventHandler(checkForTime_Elapsed); 
checkForTime.Enabled = true; 

e poi nel vostro gestore di eventi:

void checkForTime_Elapsed(object sender, ElapsedEventArgs e) 
{ 
    if (timeIsReady()) 
    { 
     SendEmail(); 
    } 
} 
+0

Pensato prima. :). Ma il mio programma funzionerà continuamente, e voglio sapere un modo alternativo, se c'è :) – Vimvq1987

+0

è un'app Winform. Cercherò di convincere il mio capo a cambiarne il design, ma all'inizio dovrei provare a soddisfare i suoi requisiti: p – Vimvq1987

+0

Grazie per la risposta. Ho aggiunto alcune informazioni. Sono molto apprezzato se puoi aiutare ... – Vimvq1987

4

Il metodo migliore che io sappia e probabilmente il più semplice è quello di utilizzare la pianificazione di Windows per eseguire il vostro codice in un momento specifico del giorno o l'applicazione viene eseguita in modo permanente e verificare una determinata ora del giorno o scrivere un servizio di Windows che fa lo stesso.

1

Se si desidera eseguire un eseguibile, utilizzare Attività pianificate di Windows. Assumerò (forse erroneamente) che vuoi che un metodo venga eseguito nel tuo programma corrente.

Perché non inserire un thread in esecuzione in modo continuo per memorizzare l'ultima data in cui è stato chiamato il metodo?

Svegliarsi ogni minuto (ad esempio) e, se l'ora corrente è maggiore del tempo specificato e l'ultima data memorizzata non è la data corrente, chiamare il metodo, quindi aggiornare la data.

8

Come altri hanno già detto, è possibile utilizzare un'app console per l'esecuzione quando pianificata. Ciò che altri non hanno detto è che questa app può attivare un processo incrociato EventWaitHandle che stai aspettando nell'applicazione principale.

Console App:

class Program 
{ 
    static void Main(string[] args) 
    { 
     EventWaitHandle handle = 
      new EventWaitHandle(true, EventResetMode.ManualReset, "GoodMutexName"); 
     handle.Set(); 
    } 
} 

principale App:

private void Form1_Load(object sender, EventArgs e) 
{ 
    // Background thread, will die with application 
    ThreadPool.QueueUserWorkItem((dumby) => EmailWait()); 
} 

private void EmailWait() 
{ 
    EventWaitHandle handle = 
     new EventWaitHandle(false, EventResetMode.ManualReset, "GoodMutexName"); 

    while (true) 
    { 
     handle.WaitOne(); 

     SendEmail(); 

     handle.Reset(); 
    } 
} 
8

Ogni volta che ho costruire applicazioni che richiedono tale funzionalità, ho uso sempre la pianificazione di Windows attraverso un semplice .NET libreria che ho trovato.

Si prega di see my answer to a similar question per alcuni codice di esempio e ulteriori spiegazioni.

4

So che questo è vecchio, ma come su questo:

Costruire un timer a fuoco in fase di avvio che calcola il tempo per la prossima fase di esecuzione. Alla prima chiamata del runtime, cancella il primo timer e avvia un nuovo timer giornaliero. cambia ogni giorno ad ogni ora o qualunque cosa tu voglia che la periodicità sia.

+8

E guardarlo fallire durante le modifiche all'ora legale. . . –

+1

Buon punto, anche se può essere compensato. – Brent

0

Piuttosto che impostare un tempo per eseguire ogni secondo di ogni 60 minuti, è possibile calcolare il tempo rimanente e impostare il timer su metà (o qualche altra frazione) di questo. In questo modo non controlli il tempo tanto ma mantieni anche un grado di accuratezza quanto l'intervallo del timer si riduce più ti avvicini al tuo tempo target.

Per esempio, se si voleva fare qualcosa di 60 minuti da ora gli intervalli timer sarebbero aproximatly:

30:00:00, 15:00:00, 07:30:00, 03:45:00 , ..., 00:00:01, CORSA!

Uso il seguente codice per riavviare automaticamente un servizio una volta al giorno. Uso un thread perché ritengo che i timer siano inaffidabili per lunghi periodi, mentre in questo esempio è più costoso, è l'unico creato per questo scopo, quindi non importa.

(fabbricato da VB.NET)

autoRestartThread = new System.Threading.Thread(autoRestartThreadRun); 
autoRestartThread.Start(); 

...

private void autoRestartThreadRun() 
{ 
    try { 
     DateTime nextRestart = DateAndTime.Today.Add(CurrentSettings.AutoRestartTime); 
     if (nextRestart < DateAndTime.Now) { 
      nextRestart = nextRestart.AddDays(1); 
     } 

     while (true) { 
      if (nextRestart < DateAndTime.Now) { 
       LogInfo("Auto Restarting Service"); 
       Process p = new Process(); 
       p.StartInfo.FileName = "cmd.exe"; 
       p.StartInfo.Arguments = string.Format("/C net stop {0} && net start {0}", "\"My Service Name\""); 
       p.StartInfo.LoadUserProfile = false; 
       p.StartInfo.UseShellExecute = false; 
       p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; 
       p.StartInfo.CreateNoWindow = true; 
       p.Start(); 
      } else { 
       dynamic sleepMs = Convert.ToInt32(Math.Max(1000, nextRestart.Subtract(DateAndTime.Now).TotalMilliseconds/2)); 
       System.Threading.Thread.Sleep(sleepMs); 
      } 
     } 
    } catch (ThreadAbortException taex) { 
    } catch (Exception ex) { 
     LogError(ex); 
    } 
} 

Nota ho impostato un intervallo mininum di 1000 ms, questo potrebbe essere increaded, ridotto o rimosso a seconda della precisione che richiedi.

Ricordarsi di interrompere anche il thread/timer quando l'applicazione si chiude.

1

Ho appena scritto un'app C# che doveva essere riavviata ogni giorno. Mi rendo conto che questa domanda è vecchia, ma non credo che faccia male aggiungere un'altra possibile soluzione. È così che ho gestito i riavvii giornalieri in un momento specifico.

public void RestartApp() 
{ 
    AppRestart = AppRestart.AddHours(5); 
    AppRestart = AppRestart.AddMinutes(30); 
    DateTime current = DateTime.Now; 
    if (current > AppRestart) { AppRestart = AppRestart.AddDays(1); } 

    TimeSpan UntilRestart = AppRestart - current; 
    int MSUntilRestart = Convert.ToInt32(UntilRestart.TotalMilliseconds); 

    tmrRestart.Interval = MSUntilRestart; 
    tmrRestart.Elapsed += tmrRestart_Elapsed; 
    tmrRestart.Start(); 
} 

per assicurare il vostro timer viene mantenuto in ambito vi consiglio di creare al di fuori del metodo utilizzando System.Timers.Timer tmrRestart = new System.Timers.Timer() metodo. Inserisci il metodo RestartApp() nel tuo evento di caricamento del modulo. Quando l'applicazione si avvia imposterà i valori per AppRestart se current è maggiore del tempo di riavvio aggiungiamo 1 giorno a AppRestart per garantire che il riavvio avvenga in tempo e che non si ottenga un'eccezione per inserire un valore negativo nel timer. Nell'evento tmrRestart_Elapsed, esegui tutto il codice necessario in quel momento specifico. Se la tua applicazione si riavvia da sola, non devi necessariamente fermare il timer, ma non fa male, Se l'applicazione non si riavvia, chiama nuovamente il metodo RestartApp() e sarai pronto per partire.

-1

Potrebbe essere solo io ma sembrava che la maggior parte di queste risposte non fosse completa o non funzionasse correttamente. Ho fatto qualcosa molto veloce e sporco. Ciò detto non è sicuro di quanto buona sia l'idea di farlo in questo modo, ma funziona perfettamente ogni volta.

while (true) 
{ 
    if(DateTime.Now.ToString("HH:mm") == "22:00") 
    { 
     //do something here 
     //ExecuteFunctionTask(); 
     //Make sure it doesn't execute twice by pausing 61 seconds. So that the time is past 2200 to 2201 
     Thread.Sleep(61000); 
    } 

    Thread.Sleep(10000); 
} 
0

Ho un approccio semplice a questo. Questo crea un ritardo di 1 minuto prima che l'azione si verifichi. È possibile aggiungere secondi per creare Thread.Sleep(); più breve.

private void DoSomething(int aHour, int aMinute) 
{ 
    bool running = true; 
    while (running) 
    { 
     Thread.Sleep(1); 
     if (DateTime.Now.Hour == aHour && DateTime.Now.Minute == aMinute) 
     { 
      Thread.Sleep(60 * 1000); //Wait a minute to make the if-statement false 
      //Do Stuff 
     } 
    } 
} 
2

Ho creato un semplice programma di pianificazione che è facile da usare e non è necessario utilizzare la libreria esterna. TaskScheduler è un singleton che mantiene i riferimenti sui timer in modo che i timer non vengano raccolti, può pianificare più attività. È possibile impostare la prima esecuzione (ora e minuti), se al momento della pianificazione questa volta è in corso l'avvio della pianificazione il giorno successivo in quel momento. Ma è facile personalizzare il codice.

Pianificare una nuova attività è così semplice. Esempio: Alle 11:52 il primo compito è per ogni 15 secondi, il secondo esempio è per ogni 5 secondi. Per l'esecuzione giornaliera impostare 24 sul parametro 3.

TaskScheduler.Instance.ScheduleTask(11, 52, 0.00417, 
    () => 
    { 
     Debug.WriteLine("task1: " + DateTime.Now); 
     //here write the code that you want to schedule 
    }); 

TaskScheduler.Instance.ScheduleTask(11, 52, 0.00139, 
    () => 
    { 
     Debug.WriteLine("task2: " + DateTime.Now); 
     //here write the code that you want to schedule 
    }); 

La mia finestra di debug:

task2: 07.06.2017 11:52:00 
task1: 07.06.2017 11:52:00 
task2: 07.06.2017 11:52:05 
task2: 07.06.2017 11:52:10 
task1: 07.06.2017 11:52:15 
task2: 07.06.2017 11:52:15 
task2: 07.06.2017 11:52:20 
task2: 07.06.2017 11:52:25 
... 

Basta aggiungere questa classe per il vostro progetto:

public class TaskScheduler 
{ 
    private static TaskScheduler _instance; 
    private List<Timer> timers = new List<Timer>(); 

    private TaskScheduler() { } 

    public static TaskScheduler Instance => _instance ?? (_instance = new TaskScheduler()); 

    public void ScheduleTask(int hour, int min, double intervalInHour, Action task) 
    { 
     DateTime now = DateTime.Now; 
     DateTime firstRun = new DateTime(now.Year, now.Month, now.Day, hour, min, 0, 0); 
     if (now > firstRun) 
     { 
      firstRun = firstRun.AddDays(1); 
     } 

     TimeSpan timeToGo = firstRun - now; 
     if (timeToGo <= TimeSpan.Zero) 
     { 
      timeToGo = TimeSpan.Zero; 
     } 

     var timer = new Timer(x => 
     { 
      task.Invoke(); 
     }, null, timeToGo, TimeSpan.FromHours(intervalInHour)); 

     timers.Add(timer); 
    } 
} 
0

Ecco un modo per farlo utilizzando TPL.Non c'è bisogno di creare/smaltire un timer, ecc:

void ScheduleSomething() 
{ 

    var runAt = DateTime.Today + TimeSpan.FromHours(16); 

    if (runAt <= DateTime.Now) 
    { 
     DoSomething(); 
    } 
    else 
    { 
     var delay = runAt - DateTime.Now; 
     System.Threading.Tasks.Task.Delay(delay).ContinueWith(_ => DoSomething()); 
    } 

} 

void DoSomething() 
{ 
    // do somethig 
} 
Problemi correlati