Я искал на SO и нашел ответы о Quartz.net. Но для моего проекта это кажется слишком большим. Мне нужно эквивалентное решение, но более простое и (в лучшем случае) в коде (внешняя библиотека не требуется). Как я могу вызывать метод ежедневно в определенное время?
Мне нужно добавить информацию об этом:
- Самый простой (и уродливый) способ сделать это - проверять время каждую секунду / минуту и вызывать метод в нужное время
Мне нужен более эффективный способ сделать это, не нужно постоянно проверять время, и я могу контролировать, выполняется ли работа или нет. Если метод не работает (из-за каких-либо проблем), программа должна знать, что нужно писать в журнал / отправлять электронное письмо. Вот почему мне нужно вызывать метод, а не планировать работу.
Я нашел это решение. Вызов метода в фиксированное время в Java на Java. Есть ли аналогичный способ в C #?
РЕДАКТИРОВАТЬ: Я сделал это. Я добавил параметр в void Main () и создал летучую мышь (запланированную планировщиком задач Windows) для запуска программы с этим параметром. Программа запускается, выполняет задание и завершает работу. Если задание не удается, оно способно вести журнал и отправлять электронную почту. Такой подход хорошо соответствует моим требованиям :)
источник
Ответы:
Это действительно все, что вам нужно!
Обновление: если вы хотите сделать это внутри своего приложения, у вас есть несколько вариантов:
Application.Idle
событие и проверить, достигли ли вы времени дня для вызова вашего метода. Этот метод вызывается только тогда, когда ваше приложение не занято другими вещами. Я думаю, быстрая проверка, чтобы увидеть, было ли достигнуто целевое время, не должна слишком сильно напрягать ваше приложение ...Обновление №2 : если вы хотите проверять каждые 60 минут, вы можете создать таймер, который просыпается каждые 60 минут, и если время истекло, он вызывает метод.
Что-то вроде этого:
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;
а затем в вашем обработчике событий:
void checkForTime_Elapsed(object sender, ElapsedEventArgs e) { if (timeIsReady()) { SendEmail(); } }
источник
timeIsReady()
звонок?Я создал простой планировщик, которым легко пользоваться, и вам не нужно использовать внешнюю библиотеку. TaskScheduler - это синглтон, который хранит ссылки на таймеры, поэтому таймеры не будут собирать мусор, он может планировать несколько задач. Вы можете установить первый запуск (час и минута), если на момент планирования это время превышает запланированный запуск на следующий день, то есть в это время. Но код легко настроить.
Планировать новую задачу так просто. Пример: в 11:52 первая задача - каждые 15 секунд, вторая - каждые 5 секунд. Для ежедневного выполнения установите 24 для параметра 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 });
Мое окно отладки:
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 ...
Просто добавьте этот класс в свой проект:
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); } }
источник
Когда я создаю приложения, требующие такой функциональности, я всегда использую планировщик задач Windows через простую библиотеку .NET, которую я нашел.
Пожалуйста, посмотрите мой ответ на аналогичный вопрос, где вы найдете пример кода и дополнительные объяснения.
источник
Как уже говорили другие, вы можете использовать консольное приложение для запуска по расписанию. Другие не сказали, что это приложение может запускать перекрестный процесс EventWaitHandle, который вы ожидаете в своем основном приложении.
Консольное приложение:
class Program { static void Main(string[] args) { EventWaitHandle handle = new EventWaitHandle(true, EventResetMode.ManualReset, "GoodMutexName"); handle.Set(); } }
Главное приложение:
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(); } }
источник
Лучший способ, о котором я знаю, и, вероятно, самый простой - это использовать планировщик задач Windows для выполнения вашего кода в определенное время дня или для того, чтобы приложение работало постоянно и проверяло определенное время дня, или написать службу Windows, которая выполняет одна и та же.
источник
Я знаю, что это старое, но как насчет этого:
Создайте таймер для запуска при запуске, который рассчитывает время до следующего запуска. При первом вызове среды выполнения отмените первый таймер и запустите новый дневной таймер. измените ежедневную на почасовую или любую желаемую периодичность.
источник
Вот способ сделать это с помощью TPL. Нет необходимости создавать / удалять таймер и т. Д .:
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 }
источник
Эта маленькая программа должна стать решением ;-)
Надеюсь, это всем поможет.
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(); } } } }
источник
Если вы хотите, чтобы исполняемый файл запускался, используйте запланированные задачи Windows. Я собираюсь предположить (возможно, ошибочно), что вы хотите, чтобы метод выполнялся в вашей текущей программе.
Почему бы просто не запустить поток, постоянно сохраняющий дату последнего вызова метода?
Пусть он просыпается каждую минуту (например), и, если текущее время больше указанного, а последняя сохраненная дата не является текущей датой, вызовите метод и обновите дату.
источник
Возможно, это только я, но казалось, что большинство этих ответов не были полными или не будут работать правильно. Я сделал что-то очень быстрое и грязное. При этом не уверен, насколько хороша идея сделать это таким образом, но она работает отлично каждый раз.
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); }
источник
Я недавно написал приложение на С #, которое нужно было перезапускать ежедневно. Я понимаю, что это старый вопрос, но не думаю, что добавление другого возможного решения повредит. Вот как я обрабатывал ежедневные перезапуски в указанное время.
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(); }
Чтобы ваш таймер оставался в области видимости, я рекомендую создавать его вне метода using
System.Timers.Timer tmrRestart = new System.Timers.Timer()
method. Поместите методRestartApp()
в событие загрузки формы. Когда приложение запускается, оно устанавливает значения дляAppRestart
ifcurrent
больше, чем время перезапуска, мы добавляем 1 день, чтобыAppRestart
гарантировать, что перезапуск произойдет вовремя и что мы не получим исключения для установки отрицательного значения в таймер. В этомtmrRestart_Elapsed
случае запустите любой код, который вам нужен, в это конкретное время. Если ваше приложение перезапускается само по себе, вам не обязательно останавливать таймер, но это тоже не повредит. Если приложение не перезапускается, просто вызовитеRestartApp()
метод снова, и все будет в порядке.источник
Я нашел это очень полезным:
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(); } } }
источник
Попробуйте использовать планировщик задач Windows. Создайте исполняемый файл, который не запрашивает ввод данных пользователем.
https://docs.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-start-page
источник
Вместо того, чтобы устанавливать время для запуска каждую секунду каждые 60 минут, вы можете рассчитать оставшееся время и установить таймер на половину (или другую долю) от этого времени. Таким образом, вы не столько проверяете время, сколько сохраняете степень точности, поскольку интервал таймера уменьшается по мере приближения к целевому времени.
Например, если вы хотите сделать что-то через 60 минут, интервалы таймера будут примерно такими:
30:00:00, 15:00:00, 07:30:00, 03:45:00, ..., 00:00:01, БЕГ!
Я использую приведенный ниже код для автоматического перезапуска службы один раз в день. Я использую поток, потому что обнаружил, что таймеры ненадежны в течение длительного времени, хотя в данном примере это дороже, это единственный созданный для этой цели, поэтому это не имеет значения.
(Преобразовано из 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); } }
Обратите внимание: я установил минимальный интервал в 1000 мс, его можно увеличить, уменьшить или удалить в зависимости от требуемой точности.
Не забудьте также остановить поток / таймер при закрытии приложения.
источник
У меня простой подход к этому. Это создает задержку в 1 минуту до того, как действие произойдет. Вы также можете добавить секунды, чтобы Thread.Sleep (); короче.
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 } } }
источник
24 часа в сутки
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"; }
источник
Простой пример для одной задачи:
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(); } } }
источник
Решение с 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); }
источник