Как вызвать метод ежедневно, в определенное время, в C#?

Я искал на SO и нашел ответы о Quartz.net - ... Но он кажется слишком большим для моего проекта. Я хочу эквивалентное решение, но проще и (в лучшем случае) в коде (не требуется внешняя библиотека). Как я могу вызывать метод ежедневно, в определенное время?

Мне нужно добавить некоторую информацию об этом:

  • самый простой (и уродливый) способ сделать это-проверить время каждую секунду/минуту и вызвать метод в нужное время

Я хочу более эффективный способ сделать это, не нужно постоянно проверять время, и у меня есть контроль над тем, выполнена ли работа. Если метод не удается (из-за каких-либо проблем), программа должна знать, чтобы написать войти/отправить по электронной почте. Вот почему мне нужно вызвать метод, а не планировать работу.

Я нашел это решение вызовите метод в фиксированное время в Java в Java. Есть ли аналогичный способ В C#?

EDIT: я сделал это. Я добавил параметр в void Main (), и создал bat (запланированный планировщиком задач Windows) для запуска программы с этим параметром. Программа запускается, выполняет задание, а затем завершает работу. Если задание терпит неудачу, оно способно записывать журнал и отправлять электронную почту. Этот подход хорошо соответствует моим требованиям:)

13 ответов


  • создать консольное приложение, которое делает то, что вы ищете
  • использовать Windows "Запланированные Задачи" функциональность, чтобы это консольное приложение выполнялось в то время, когда вам это нужно для запуска

это действительно все, что вам нужно!

обновление: если вы хотите сделать это в своем приложении, у вас есть несколько вариантов:

  • на Windows Forms app, вы можете нажать на Application.Idle событие и проверьте, достигли ли Вы времени в день, чтобы вызвать свой метод. Этот метод вызывается только тогда, когда ваше приложение не занято другими вещами. Быстрая проверка, чтобы увидеть, если ваше целевое время было достигнуто не должно поставить слишком много стресса на вашем приложении, я думаю...
  • в a ASP.NET веб-приложение, есть методы для "имитации" отправки запланированных событий-проверьте это статья CodeProject
  • и, конечно, вы также можете просто просто "сверните свой собственный" в любом приложении .NET-проверьте это статья CodeProject для примера реализации

обновление #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();
    }
}

всякий раз, когда я создаю приложения, требующие такой функциональности, я всегда используйте планировщик задач 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, которая делает то же самое.


Я знаю, что это старый, но как насчет этого:

создайте таймер для запуска при запуске, который вычисляет время до следующего времени выполнения. При первом вызове среды выполнения отмените первый таймер и запустите новый ежедневный таймер. измените ежедневно на почасовую или любую другую периодичность.


Я создал простой планировщик, который прост в использовании и вам не нужно использовать внешние библиотеки. TaskScheduler-это одноэлемент, который хранит ссылки на таймеры, поэтому таймеры не будут собираться мусором, он может планировать несколько задач. Вы можете установить первый запуск (час и минуту), если на момент планирования это время закончилось, планирование начнется на следующий день в это время. Но легко настроить код.

планирование новой задачи так просто. Пример: в 11: 52 первая задача-за каждые 15 secunds, второй пример за каждые 5 secunds. Для ежедневного выполнения установите 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. Я собираюсь предположить (возможно, ошибочно), что вы хотите, чтобы метод работал в вашей текущей программе.

Почему бы просто не запустить поток, постоянно хранящий последнюю дату, когда был вызван метод?

пусть он просыпается каждую минуту (например), и если текущее время больше указанного времени, а последняя сохраненная дата не является текущей датой, вызовите метод, затем обновите дата.


недавно я написал приложение c#, которое пришлось перезапускать ежедневно. Я понимаю, что этот вопрос старый, но я не думаю, что больно добавлять другое возможное решение. Вот как я обрабатывал ежедневные перезагрузки в указанное время.

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

чтобы убедиться, что ваш таймер хранится в области, я рекомендую создать его вне метода с помощью System.Timers.Timer tmrRestart = new System.Timers.Timer() метод. Положите метод RestartApp() в событии load формы. При запуске приложения будет установлено значение AppRestart Если current больше время перезапуска мы добавляем 1 день в AppRestart для обеспечения перезапуска происходит вовремя, и что мы не получаем исключение для ввода отрицательного значения в таймер. В tmrRestart_Elapsed событие запустите любой код, который вам нужен, в это конкретное время. Если ваше приложение перезапускается самостоятельно, вам не обязательно останавливать таймер, но это тоже не больно, если приложение не перезапускается, просто вызовите RestartApp() метод снова, и вам будет хорошо идти.


вот способ сделать это с помощью 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
}

вместо того, чтобы устанавливать время для запуска каждую секунду каждые 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);
    }
}

Примечание я установил интервал mininum 1000 мс, это может быть увеличено, уменьшено или удалено в зависимости от accurcy вы требуете.

Не забудьте также остановить поток/таймер при закрытии приложения.


У меня простой подход к этому. Это создает 1-минутную задержку, прежде чем произойдет действие. Вы также можете добавить секунды, чтобы сделать поток.Сон(); короче.

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

это может быть просто я, но казалось, что большинство из этих ответов не были полными или не будут работать правильно. Я сделал что-то очень быстрое и грязное. При этом не уверен, насколько хороша идея сделать это таким образом, но она отлично работает каждый раз.

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

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