Szukałem w SO i znalazłem odpowiedzi na temat Quartz.net. Ale wydaje się, że jest za duży dla mojego projektu. Chcę równoważnego rozwiązania, ale prostszego i (w najlepszym przypadku) w kodzie (nie jest wymagana żadna zewnętrzna biblioteka). Jak mogę wywoływać metodę codziennie, o określonej godzinie?
Muszę dodać kilka informacji na ten temat:
- najprostszym (i brzydkim) sposobem na to jest sprawdzanie czasu co sekundę / minutę i wywoływanie metody we właściwym czasie
Chcę skuteczniejszego sposobu, aby to zrobić, bez konieczności ciągłego sprawdzania czasu i mam kontrolę nad tym, czy praca jest wykonana, czy nie. Jeśli metoda nie powiedzie się (z powodu jakichkolwiek problemów), program powinien wiedzieć, aby zapisać do logowania / wysłać wiadomość e-mail. Dlatego muszę wywołać metodę, a nie zaplanować pracę.
Znalazłem to rozwiązanie. Wywołaj metodę w ustalonym czasie w Javie w Javie. Czy jest podobny sposób w C #?
EDYCJA: Zrobiłem to. Dodałem parametr do void Main () i utworzyłem bat (zaplanowany przez Harmonogram zadań systemu Windows), aby uruchomić program z tym parametrem. Program działa, wykonuje swoją pracę, a następnie kończy pracę. Jeśli zadanie się nie powiedzie, jest w stanie zapisać dziennik i wysłać e-mail. Takie podejście dobrze pasuje do moich wymagań :)
źródło
Odpowiedzi:
To naprawdę wszystko, czego potrzebujesz!
Aktualizacja: jeśli chcesz to zrobić w swojej aplikacji, masz kilka opcji:
Application.Idle
zdarzenia i sprawdzić, czy osiągnąłeś odpowiednią porę dnia na wywołanie metody. Ta metoda jest wywoływana tylko wtedy, gdy Twoja aplikacja nie jest zajęta innymi rzeczami. Szybkie sprawdzenie, czy osiągnięto docelowy czas, nie powinno zbytnio obciążać Twojej aplikacji, myślę, że ...Aktualizacja nr 2 : jeśli chcesz sprawdzać co 60 minut, możesz utworzyć licznik czasu, który budzi się co 60 minut, a jeśli czas się skończy, wywołuje metodę.
Coś takiego:
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;
a następnie w programie obsługi zdarzeń:
void checkForTime_Elapsed(object sender, ElapsedEventArgs e) { if (timeIsReady()) { SendEmail(); } }
źródło
timeIsReady()
rozmowa?Stworzyłem prosty harmonogram, który jest łatwy w użyciu i nie wymaga korzystania z zewnętrznej biblioteki. TaskScheduler to singleton, który przechowuje odwołania do timerów, dzięki czemu timery nie będą zbierane jako śmieci, ale może zaplanować wiele zadań. Można ustawić pierwsze uruchomienie (godzinę i minutę), jeśli w momencie planowania ten czas się skończył, planowanie rozpocznie się następnego dnia o tej godzinie. Ale łatwo jest dostosować kod.
Planowanie nowego zadania jest takie proste. Przykład: O 11:52 pierwsze zadanie jest wykonywane co 15 sekund, a drugi przykład co 5 sekund. Do codziennego wykonywania należy ustawić parametr 24 na 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 });
Moje okno debugowania:
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 ...
Po prostu dodaj tę klasę do swojego projektu:
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); } }
źródło
Zawsze, gdy tworzę aplikacje, które wymagają takiej funkcjonalności, zawsze korzystam z Harmonogramu zadań systemu Windows za pośrednictwem prostej biblioteki .NET , którą znalazłem.
Proszę zobaczyć moją odpowiedź na podobne pytanie dla niektórych przykładowy kod i więcej wyjaśnień.
źródło
Jak powiedzieli inni, możesz użyć aplikacji konsoli do uruchamiania zgodnie z harmonogramem. Inni nie powiedzieli, że ta aplikacja może wywołać proces EventWaitHandle, na który czekasz w swojej głównej aplikacji.
Aplikacja konsolowa:
class Program { static void Main(string[] args) { EventWaitHandle handle = new EventWaitHandle(true, EventResetMode.ManualReset, "GoodMutexName"); handle.Set(); } }
Główna aplikacja:
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(); } }
źródło
Najlepszą metodą, jaką znam i prawdopodobnie najprostszą, jest użycie Harmonogramu zadań systemu Windows do wykonania kodu o określonej porze dnia lub uruchomienie aplikacji na stałe i sprawdzenie określonej porze dnia lub napisanie usługi systemu Windows, która wykonuje podobnie.
źródło
Wiem, że to jest stare, ale co powiesz na to:
Zbuduj licznik czasu, który będzie uruchamiany podczas uruchamiania, który oblicza czas do następnego uruchomienia. Przy pierwszym wywołaniu środowiska wykonawczego anuluj pierwszy zegar i uruchom nowy dzienny licznik czasu. zmieniać codziennie na co godzinę lub cokolwiek chcesz, aby okresowość była.
źródło
Oto sposób na zrobienie tego za pomocą TPL. Nie ma potrzeby tworzenia / pozbywania się timera itp .:
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 }
źródło
Ten mały program powinien być rozwiązaniem ;-)
Mam nadzieję, że to pomoże wszystkim.
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(); } } } }
źródło
Jeśli chcesz, aby plik wykonywalny działał, użyj Zaplanowanych zadań systemu Windows. Założę (być może błędnie), że chcesz, aby metoda działała w Twoim bieżącym programie.
Dlaczego po prostu nie uruchomić wątku w sposób ciągły przechowujący ostatnią datę wywołania metody?
Niech budzi się co minutę (na przykład) i, jeśli bieżący czas jest dłuższy niż określony czas, a ostatnia zapisana data nie jest aktualną datą, wywołaj metodę, a następnie zaktualizuj datę.
źródło
Może to tylko ja, ale wydawało się, że większość z tych odpowiedzi nie była kompletna lub nie będzie działać poprawnie. Zrobiłem coś bardzo szybko i brudno. Biorąc to pod uwagę, nie jestem pewien, jak dobry jest pomysł zrobienia tego w ten sposób, ale za każdym razem działa idealnie.
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); }
źródło
Niedawno napisałem aplikację ac #, która musiała być codziennie restartowana. Zdaję sobie sprawę, że to pytanie jest stare, ale nie sądzę, by boli dodanie kolejnego możliwego rozwiązania. W ten sposób radziłem sobie z codziennymi restartami o określonej godzinie.
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(); }
Aby mieć pewność, że licznik czasu jest utrzymywany w zakresie, zalecam utworzenie go poza metodą przy użyciu
System.Timers.Timer tmrRestart = new System.Timers.Timer()
metody. Umieść metodęRestartApp()
w zdarzeniu ładowania formularza. Gdy aplikacja zostanie uruchomiona, ustawi wartości,AppRestart
jeślicurrent
jest większe niż czas ponownego uruchomienia, dodamy 1 dzień,AppRestart
aby upewnić się, że ponowne uruchomienie nastąpi na czas i że nie otrzymamy wyjątku od umieszczenia wartości ujemnej w liczniku czasu. WtmrRestart_Elapsed
przypadku uruchomienia dowolnego kodu, którego potrzebujesz, uruchomiono w tym określonym czasie. Jeśli Twoja aplikacja uruchomi się ponownie samoczynnie, niekoniecznie musisz zatrzymywać licznik czasu, ale to też nie zaszkodzi.Jeśli aplikacja nie uruchomi się ponownie, po prostu wywołajRestartApp()
metodę ponownie, a będziesz gotowy do pracy.źródło
Uważam, że to bardzo przydatne:
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(); } } }
źródło
Spróbuj użyć Harmonogramu zadań systemu Windows. Utwórz plik exe, który nie monituje o żadne dane wejściowe użytkownika.
https://docs.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-start-page
źródło
Zamiast ustawiać czas uruchamiania co sekundę co 60 minut, możesz obliczyć pozostały czas i ustawić licznik na połowę (lub inną część) tego. W ten sposób nie sprawdzasz czasu tak często, ale także zachowujesz pewien stopień dokładności, ponieważ interwał timera zmniejsza się, im bliżej jesteś czasu docelowego.
Na przykład, jeśli chciałbyś zrobić coś za 60 minut od teraz, interwały timerów byłyby w przybliżeniu:
30:00:00, 15:00:00, 07:30:00, 03:45:00, ..., 00:00:01, RUN!
Używam poniższego kodu, aby automatycznie ponownie uruchamiać usługę raz dziennie. Używam wątku, ponieważ stwierdziłem, że liczniki czasu są zawodne przez długi czas, podczas gdy w tym przykładzie jest to bardziej kosztowne, jest to jedyny utworzony w tym celu, więc nie ma to znaczenia.
(Przekonwertowane z 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); } }
Uwaga Ustawiłem minimalny interwał 1000 ms, który można zwiększyć, zmniejszyć lub usunąć w zależności od wymaganej dokładności.
Pamiętaj, aby zatrzymać wątek / licznik czasu po zamknięciu aplikacji.
źródło
Mam do tego proste podejście. Powoduje to 1-minutowe opóźnienie przed rozpoczęciem działania. Możesz również dodać sekundy, aby Thread.Sleep (); krótszy.
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 } } }
źródło
24 godziny razy
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"; }
źródło
Prosty przykład jednego zadania:
using System; using System.Timers; namespace ConsoleApp { internal class Scheduler { private static readonly DateTime scheduledTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 10, 0, 0); private static DateTime dateTimeLastRunTask; internal static void CheckScheduledTask() { if (dateTimeLastRunTask.Date < DateTime.Today && scheduledTime.TimeOfDay < DateTime.Now.TimeOfDay) { Console.WriteLine("Time to run task"); dateTimeLastRunTask = DateTime.Now; } else { Console.WriteLine("not yet time"); } } } internal class Program { private static Timer timer; static void Main(string[] args) { timer = new Timer(5000); timer.Elapsed += OnTimer; timer.Start(); Console.ReadLine(); } private static void OnTimer(object source, ElapsedEventArgs e) { Scheduler.CheckScheduledTask(); } } }
źródło
Rozwiązanie z 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); }
źródło