Wie rufe ich eine Methode täglich zu einer bestimmten Zeit in C # auf?

83

Ich habe auf SO gesucht und Antworten zu Quartz.net gefunden. Aber es scheint zu groß für mein Projekt zu sein. Ich möchte eine gleichwertige Lösung, aber einfacher und (bestenfalls) im Code (keine externe Bibliothek erforderlich). Wie kann ich eine Methode täglich zu einer bestimmten Zeit aufrufen?

Ich muss einige Informationen dazu hinzufügen:

  • Der einfachste (und hässlichste) Weg, dies zu tun, besteht darin, die Zeit jede Sekunde / Minute zu überprüfen und die Methode zum richtigen Zeitpunkt aufzurufen

Ich möchte einen effektiveren Weg, um dies zu tun, ohne die Zeit ständig überprüfen zu müssen, und ich habe die Kontrolle darüber, ob die Arbeit erledigt ist oder nicht. Wenn die Methode fehlschlägt (aufgrund von Problemen), sollte das Programm wissen, wie man eine E-Mail protokolliert / sendet. Deshalb muss ich eine Methode aufrufen und keinen Job planen.

Ich habe diese Lösung gefunden. Rufen Sie eine Methode zur festgelegten Zeit in Java in Java auf. Gibt es einen ähnlichen Weg in C #?

EDIT: Ich habe das getan. Ich habe einen Parameter in void Main () eingefügt und einen Bat (geplant von Windows Task Scheduler) erstellt, um das Programm mit diesem Parameter auszuführen. Das Programm wird ausgeführt, erledigt die Aufgabe und wird dann beendet. Wenn ein Auftrag fehlschlägt, kann er ein Protokoll schreiben und E-Mails senden. Dieser Ansatz passt gut zu meinen Anforderungen :)

Quan Mai
quelle
2
Diese verknüpfte Frage scheint darauf hinzudeuten, dass eine Methode in Ihrer laufenden App regelmäßig aufgerufen werden muss. Ist das der Fall? Dies wirkt sich darauf aus, ob Sie eine In-Process-Planung benötigen oder ob Sie nur den Windows-Planer verwenden können.
Paxdiablo
Mein Programm wird je nach Bedarf kontinuierlich ausgeführt
Quan Mai
Hey, ich kann nicht glauben, dass du meine Antwort "hässlich" genannt hast. Sie
kämpfen
Meinte nicht über Ihre Antwort: p. Ich dachte auch darüber nach und fand meine hässlich.
Quan Mai
Verknüpft mit stackoverflow.com/questions/725917
PaulB

Antworten:

85
  • Erstellen Sie eine Konsolen-App, die genau das tut, wonach Sie suchen
  • Verwenden Sie die Windows-Funktion " Geplante Aufgaben ", um diese Konsolen-App zum Zeitpunkt der Ausführung auszuführen

Das ist wirklich alles was du brauchst!

Update: Wenn Sie dies in Ihrer App tun möchten, haben Sie mehrere Möglichkeiten:

  • In einer Windows Forms- App können Sie auf das Application.IdleEreignis tippen und prüfen, ob Sie die Tageszeit erreicht haben, um Ihre Methode aufzurufen. Diese Methode wird nur aufgerufen, wenn Ihre App nicht mit anderen Dingen beschäftigt ist. Eine schnelle Überprüfung, ob Ihre Zielzeit erreicht wurde, sollte Ihre App nicht zu stark belasten, denke ich ...
  • In einer ASP.NET-Webanwendung gibt es Methoden zum "Simulieren" des Versendens geplanter Ereignisse. Lesen Sie diesen CodeProject-Artikel
  • Natürlich können Sie auch in jeder .NET-App einfach "Ihre eigenen rollen". In diesem CodeProject-Artikel finden Sie eine Beispielimplementierung

Update Nr. 2 : Wenn Sie alle 60 Minuten überprüfen möchten, können Sie einen Timer erstellen, der alle 60 Minuten aktiviert wird. Wenn die Zeit abgelaufen ist, wird die Methode aufgerufen.

Etwas wie das:

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;

und dann in Ihrem Event-Handler:

void checkForTime_Elapsed(object sender, ElapsedEventArgs e)
{
    if (timeIsReady())
    {
       SendEmail();
    }
}
marc_s
quelle
Ich habe vorher darüber nachgedacht. :). Aber mein Programm läuft ununterbrochen und ich möchte einen alternativen Weg kennen, wenn es einen gibt :)
Quan Mai
Es ist eine Winform-App. Ich werde versuchen, meinen Chef davon zu überzeugen, das Design zu ändern, aber zuerst sollte ich versuchen, seine Anforderungen zu erfüllen: p
Quan Mai
4
Was macht der timeIsReady()Anruf?
brimble2010
1
@ brimble2010: Es kann alles tun, was du willst. Sie könnten zB einen Tisch mit vorübergehend "blockierten" Zeitfenstern haben (z. B. nicht um 3, 4 oder 5 Uhr morgens laufen ) oder was auch immer - ganz nach Ihren Wünschen. Nur eine zusätzliche Überprüfung, zusätzlich zum Start alle 60 Minuten ....
marc_s
1
Sehr schöne, einfache, kurze und saubere Lösung, die bis heute in Windows 10 und .Net 4.7.2 hervorragend funktioniert. !! Vielen Dank.
MinimalTech
19

Ich habe einen einfachen Scheduler erstellt, der einfach zu verwenden ist und für den Sie keine externe Bibliothek benötigen. TaskScheduler ist ein Singleton, der Verweise auf die Timer speichert, damit Timer nicht durch Müll gesammelt werden. Er kann mehrere Aufgaben planen. Sie können den ersten Lauf (Stunde und Minute) einstellen, wenn zum Zeitpunkt der Planung diese Zeit abgelaufen ist. Beginnen Sie am nächsten Tag mit dieser Planung. Es ist jedoch einfach, den Code anzupassen.

Das Planen einer neuen Aufgabe ist so einfach. Beispiel: Um 11:52 Uhr ist die erste Aufgabe alle 15 Sekunden, das zweite Beispiel alle 5 Sekunden. Stellen Sie für die tägliche Ausführung 24 auf den Parameter 3 ein.

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

Mein Debug-Fenster:

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

Fügen Sie diese Klasse einfach Ihrem Projekt hinzu:

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);
    }
}
jannagy02
quelle
new Timer hat keine Methode, die 4 Argumente akzeptiert.
Fandango68
Ich nehme an, Timer hier ist von System.Timers? Könnten Sie ein Beispielarbeitsprogramm bereitstellen? Vielen Dank
Fandango68
1
@ Fandango68 Ich habe den Timer aus dem Namespace System.Threading verwendet. Es gibt einen anderen Timer in System.Timers. Ich denke, Sie haben die falsche Verwendung oben verwendet.
jannagy02
@ jannagy02 Wie plane ich eine Aufgabe für einen bestimmten Tag wie Montag?
Sruthi Varghese
9

Wenn ich Anwendungen erstelle, die solche Funktionen erfordern, verwende ich den Windows Task Scheduler immer über eine einfache .NET-Bibliothek , die ich gefunden habe.

In meiner Antwort auf eine ähnliche Frage finden Sie einen Beispielcode und weitere Erläuterungen.

Maxim Zaslavsky
quelle
8

Wie bereits erwähnt, können Sie eine Konsolen-App verwenden, um sie planmäßig auszuführen. Was andere nicht gesagt haben, ist, dass Sie mit dieser App ein prozessübergreifendes EventWaitHandle auslösen können, auf das Sie in Ihrer Hauptanwendung warten.

Konsolen-App:

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

Haupt-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();
    }
}
Courtney D.
quelle
4

Die beste Methode, die ich kenne und wahrscheinlich die einfachste, besteht darin, den Windows Task Scheduler zu verwenden, um Ihren Code zu einer bestimmten Tageszeit auszuführen, oder Ihre Anwendung dauerhaft auszuführen und nach einer bestimmten Tageszeit zu suchen oder einen Windows-Dienst zu schreiben, der das ausführt gleich.

Pieter Germishuys
quelle
4

Ich weiß, das ist alt, aber wie wäre es damit:

Erstellen Sie einen Timer, der beim Start ausgelöst wird und die Zeit bis zur nächsten Laufzeit berechnet. Brechen Sie beim ersten Aufruf der Laufzeit den ersten Timer ab und starten Sie einen neuen Tages-Timer. Wechseln Sie täglich zu stündlich oder wie auch immer die Periodizität sein soll.

Mansoor
quelle
9
Beobachten Sie, wie es bei Änderungen der Sommerzeit fehlschlägt. . .
Jim Mischel
3

Hier ist eine Möglichkeit, dies mit TPL zu tun. Keine Notwendigkeit, einen Timer zu erstellen / zu entsorgen usw.:

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
}
Gino
quelle
2

Dieses kleine Programm sollte die Lösung sein ;-)

Ich hoffe das hilft allen.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace DailyWorker
{
    class Program
    {
        static void Main(string[] args)
        {
            var cancellationSource = new CancellationTokenSource();

            var utils = new Utils();
            var task = Task.Run(
                () => utils.DailyWorker(12, 30, 00, () => DoWork(cancellationSource.Token), cancellationSource.Token));

            Console.WriteLine("Hit [return] to close!");
            Console.ReadLine();

            cancellationSource.Cancel();
            task.Wait();
        }

        private static void DoWork(CancellationToken token)
        {
            while (!token.IsCancellationRequested)
            {
                Console.Write(DateTime.Now.ToString("G"));
                Console.CursorLeft = 0;
                Task.Delay(1000).Wait();
            }
        }
    }

    public class Utils
    {
        public void DailyWorker(int hour, int min, int sec, Action someWork, CancellationToken token)
        {
            while (!token.IsCancellationRequested)
            {
                var dateTimeNow = DateTime.Now;
                var scanDateTime = new DateTime(
                    dateTimeNow.Year,
                    dateTimeNow.Month,
                    dateTimeNow.Day,
                    hour,       // <-- Hour when the method should be started.
                    min,  // <-- Minutes when the method should be started.
                    sec); // <-- Seconds when the method should be started.

                TimeSpan ts;
                if (scanDateTime > dateTimeNow)
                {
                    ts = scanDateTime - dateTimeNow;
                }
                else
                {
                    scanDateTime = scanDateTime.AddDays(1);
                    ts           = scanDateTime - dateTimeNow;
                }

                try
                {
                     Task.Delay(ts).Wait(token);
                }
                catch (OperationCanceledException)
                {
                    break;
                }

                // Method to start
                someWork();
            }
        }
    }
}
Marcus
quelle
1

Wenn eine ausführbare Datei ausgeführt werden soll, verwenden Sie Windows Scheduled Tasks. Ich gehe davon aus (vielleicht fälschlicherweise), dass Sie eine Methode in Ihrem aktuellen Programm ausführen möchten.

Warum nicht einfach einen Thread laufen lassen, der das letzte Datum, an dem die Methode aufgerufen wurde, kontinuierlich speichert?

Lassen Sie es beispielsweise jede Minute aufwachen. Wenn die aktuelle Zeit größer als die angegebene Zeit ist und das zuletzt gespeicherte Datum nicht das aktuelle Datum ist, rufen Sie die Methode auf und aktualisieren Sie das Datum.

paxdiablo
quelle
1

Es mag nur ich sein, aber es schien, als wären die meisten dieser Antworten nicht vollständig oder würden nicht richtig funktionieren. Ich habe etwas sehr schnelles und schmutziges gemacht. Davon abgesehen bin ich mir nicht sicher, wie gut es ist, es so zu machen, aber es funktioniert jedes Mal perfekt.

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);
}
Todespirscher
quelle
1
Soweit ich sehen kann, wäre dies ein arbeitsreiches Warten und daher keine gute Idee. en.wikipedia.org/wiki/Busy_waiting
Tommy Ivarsson
1

Ich habe kürzlich eine ac # App geschrieben, die täglich neu gestartet werden musste. Mir ist klar, dass diese Frage alt ist, aber ich glaube nicht, dass es weh tut, eine weitere mögliche Lösung hinzuzufügen. So habe ich tägliche Neustarts zu einem bestimmten Zeitpunkt durchgeführt.

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

Um sicherzustellen, dass Ihr Timer im Gültigkeitsbereich bleibt, empfehle ich, ihn außerhalb der Methode mithilfe der System.Timers.Timer tmrRestart = new System.Timers.Timer()Methode zu erstellen . Fügen Sie die Methode RestartApp()in Ihr Formularladeereignis ein. Beim Start der Anwendung werden die Werte für AppRestartif festgelegt, currentdie größer als die Neustartzeit sind, die wir um 1 Tag hinzufügen, AppRestartum sicherzustellen, dass der Neustart pünktlich erfolgt und keine Ausnahme für das Einfügen eines negativen Werts in den Timer auftritt. tmrRestart_ElapsedFühren Sie für den Fall den Code aus, den Sie zu diesem bestimmten Zeitpunkt benötigen. Wenn Ihre Anwendung von selbst neu gestartet wird, müssen Sie den Timer nicht unbedingt anhalten, aber es tut auch nicht weh. Wenn die Anwendung nicht neu gestartet wird, rufen Sie die RestartApp()Methode einfach erneut auf und Sie können loslegen.

Fancy_Mammoth
quelle
1

Ich fand das sehr nützlich:

using System;
using System.Timers;

namespace ScheduleTimer
{
    class Program
    {
        static Timer timer;

        static void Main(string[] args)
        {
            schedule_Timer();
            Console.ReadLine();
        }

        static void schedule_Timer()
        {
            Console.WriteLine("### Timer Started ###");

            DateTime nowTime = DateTime.Now;
            DateTime scheduledTime = new DateTime(nowTime.Year, nowTime.Month, nowTime.Day, 8, 42, 0, 0); //Specify your scheduled time HH,MM,SS [8am and 42 minutes]
            if (nowTime > scheduledTime)
            {
                scheduledTime = scheduledTime.AddDays(1);
            }

            double tickTime = (double)(scheduledTime - DateTime.Now).TotalMilliseconds;
            timer = new Timer(tickTime);
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            timer.Start();
        }

        static void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            Console.WriteLine("### Timer Stopped ### \n");
            timer.Stop();
            Console.WriteLine("### Scheduled Task Started ### \n\n");
            Console.WriteLine("Hello World!!! - Performing scheduled task\n");
            Console.WriteLine("### Task Finished ### \n\n");
            schedule_Timer();
        }
    }
}
JoKeRxbLaCk
quelle
0

Anstatt alle 60 Minuten eine Sekunde für die Ausführung festzulegen, können Sie die verbleibende Zeit berechnen und den Timer auf die Hälfte (oder einen anderen Bruchteil) davon einstellen. Auf diese Weise überprüfen Sie die Zeit nicht so genau, sondern behalten auch einen gewissen Grad an Genauigkeit bei, da sich das Zeitintervall verringert, je näher Sie Ihrer Zielzeit kommen.

Wenn Sie beispielsweise in 60 Minuten etwas tun möchten, sind die Timer-Intervalle ungefähr:

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

Ich verwende den folgenden Code, um einen Dienst einmal am Tag automatisch neu zu starten. Ich verwende einen Thread, weil ich festgestellt habe, dass Timer über lange Zeiträume unzuverlässig sind, während dies in diesem Beispiel teurer ist. Es ist der einzige, der für diesen Zweck erstellt wurde, sodass dies keine Rolle spielt.

(Von VB.NET konvertiert)

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

Hinweis Ich habe ein Mindestintervall von 1000 ms festgelegt. Dieses Intervall kann je nach der von Ihnen gewünschten Genauigkeit erhöht, verringert oder entfernt werden.

Denken Sie daran, Ihren Thread / Timer auch zu stoppen, wenn Ihre Anwendung geschlossen wird.

apc
quelle
0

Ich habe einen einfachen Ansatz. Dies führt zu einer Verzögerung von 1 Minute, bevor die Aktion ausgeführt wird. Sie können auch Sekunden hinzufügen, um Thread.Sleep () zu erstellen. kürzer.

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
        }
    }
}
Gustav Bok
quelle
0

24 Stunden mal

var DailyTime = "16:59:00";
            var timeParts = DailyTime.Split(new char[1] { ':' });

            var dateNow = DateTime.Now;
            var date = new DateTime(dateNow.Year, dateNow.Month, dateNow.Day,
                       int.Parse(timeParts[0]), int.Parse(timeParts[1]), int.Parse(timeParts[2]));
            TimeSpan ts;
            if (date > dateNow)
                ts = date - dateNow;
            else
            {
                date = date.AddDays(1);
                ts = date - dateNow;
            }

            //waits certan time and run the code
            Task.Delay(ts).ContinueWith((x) => OnTimer());

public void OnTimer()
    {
        ViewBag.ErrorMessage = "EROOROOROROOROR";
    }
Pandurang Chopade
quelle
-1

Lösung mit System.Threading.Timer:

    private void nameOfMethod()
    {
        //do something
    }

    /// <summary>
    /// run method at 22:00 every day
    /// </summary>
    private void runMethodEveryDay()
    {
        var runAt = DateTime.Today + TimeSpan.FromHours(22);

        if(runAt.Hour>=22)
            runAt = runAt.AddDays(1.00d); //if aplication is started after 22:00 

        var dueTime = runAt - DateTime.Now; //time before first run ; 

        long broj3 = (long)dueTime.TotalMilliseconds;
        TimeSpan ts2 = new TimeSpan(24, 0, 1);//period of repeating method
        long broj4 = (long)ts2.TotalMilliseconds;
        timer2 = new System.Threading.Timer(_ => nameOfMethod(), null, broj3, broj4);
    }
Zvonimir
quelle