Как вызвать метод ежедневно, в определенное время, в C #? - PullRequest
69 голосов
/ 14 июля 2010

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

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

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

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

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

РЕДАКТИРОВАТЬ: Я сделал это. Я добавил параметр в void Main () и создал bat (запланированный Windows Task Scheduler) для запуска программы с этим параметром. Программа запускается, выполняет работу, а затем завершает работу. Если работа не удалась, она может написать журнал и отправить электронную почту. Этот подход хорошо соответствует моим требованиям:)

Ответы [ 14 ]

71 голосов
/ 14 июля 2010
  • Создайте консольное приложение, которое делает то, что вы ищете
  • Используйте функцию Windows " Scheduled Tasks ", чтобы запустить это консольное приложение в нужное время.для запуска

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

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

  • в приложении Windows Forms вы можете подключиться к событию Application.Idle и проверить, достигло ли вы того дня для вызова вашего метода,Этот метод вызывается только тогда, когда ваше приложение не занято другими вещами.Быстрая проверка, чтобы увидеть, достигнуто ли ваше целевое время, не должна слишком сильно нагружать ваше приложение, я думаю ...
  • в веб-приложении 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();
    }
}
9 голосов
/ 14 июля 2010

Всякий раз, когда я создаю приложения, требующие такой функциональности, я всегда использую планировщик задач Windows через простую библиотеку .NET , которую я нашел.

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

8 голосов
/ 14 июля 2010

Как уже говорили другие, вы можете использовать консольное приложение для запуска по расписанию.Другие не сказали, что вы можете запускать кросс-процесс 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();
    }
}
6 голосов
/ 07 июня 2017

Я создал простой планировщик, который прост в использовании, и вам не нужно использовать внешнюю библиотеку. 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);
    }
}
4 голосов
/ 17 ноября 2011

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

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

4 голосов
/ 14 июля 2010

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

1 голос
/ 28 ноября 2017

Вот способ сделать это, используя 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
}
1 голос
/ 17 декабря 2016

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

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);
}
1 голос
/ 19 октября 2016

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

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() в событие загрузки формы.Когда приложение запускается, оно устанавливает значения для AppRestart, если current больше, чем время перезапуска, мы добавляем 1 день к AppRestart, чтобы гарантировать, что перезапуск происходит вовремя, и что мы не получаем исключение для помещенияотрицательное значение в таймер.В событии tmrRestart_Elapsed запустите любой необходимый код в указанное время.Если ваше приложение перезапускается само по себе, вам не обязательно останавливать таймер, но это также не повредит. Если приложение не перезапускается, просто вызовите метод RestartApp() еще раз, и вы будете в порядке.

1 голос
/ 14 июля 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...