Подтвердить что ты не робот

С# - альтернатива System.Timers.Timer, для вызова функции в определенное время

Я хочу вызвать определенную функцию в моем приложении С# в определенное время. Сначала я подумал об использовании Timer (System.Time.Timer), но это стало практически невозможно использовать. Почему?

Simple. Для класса Timer требуется Interval в миллисекундах, но, учитывая, что я могу захотеть, чтобы функция была выполнена, пусть говорит через неделю, что будет означать:

  • 7 дней = 168 часов;
  • 168 часов = 10 080 минут;
  • 10,080 минут = 604 800 секунд;
  • 604 800 секунд = 604 800 000 миллисекунд;
  • Таким образом, интервал составит 604 800 000,

Теперь не забывайте, что принятый тип данных Interval - int, и, как известно, диапазон int изменяется от -2147,483,648 до 2,147,483,647.

Это делает Timer бесполезным, но не в этом случае, но в случае более чем 25 дней, как только мы не сможем установить Interval больше, чем 2 147 483 647 миллисекунд.


Поэтому мне нужно решение, в котором я мог бы указать, когда должна быть вызвана функция. Что-то вроде этого:

solution.ExecuteAt = "30-04-2010 15:10:00";
solution.Function = "functionName";
solution.Start();

Итак, когда системное время достигнет "30-04-2010 15:10:00", функция будет выполнена в приложении.

Как решить эту проблему?


Дополнительная информация: что сделают эти функции?

  • Получение климатической информации и на основе этой информации:
  • Запуск/выключение других приложений (большинство из них основаны на консоли);
  • Отправка пользовательских команд в консольные приложения;
  • Отключение, перезагрузка, спящий режим, спящий режим компьютера;
  • И если возможно, заплатите BIOS за включение компьютера;

EDIT:

Казалось бы, что принятый тип данных Interval - double, но если вы установите значение больше, чем int на Interval, а вызов Start(), он выдает исключение [0, Int32.MaxValue].

ИЗМЕНИТЬ 2:

Jørn Schou-Rode предложил использовать Ncron для обработки задач планирования, и сначала это кажется хорошим решением, но я бы как услышать о тех, кто работал с ним.

4b9b3361

Ответ 1

Один подход к планированию задач, аналогичный предложенному klausbyskov, заключается в построении службы планирования поверх существующей платформы/библиотеки планирования .NET. По сравнению с использованием планировщика заданий Windows это имеет следующие преимущества: (а) позволяет определить несколько заданий в одном проекте и (б) сохранять задания и логику планирования "вместе" - то есть не полагаться на настройки сервера, которые могут затеряться в обновления/замены системы.

Я знаю два проекта с открытым исходным кодом, которые предлагают такую ​​функциональность:

  • " Quartz.NET - полнофункциональная система планирования заданий с открытым исходным кодом, которая может использоваться из самых маленьких приложений на большие масштабных корпоративных систем". Я никогда не использовал эту структуру самостоятельно, но, изучая веб-сайт, у меня создается впечатление очень прочного инструмента, предоставляющего много интересных функций. Тот факт, что тег [quartz-net] в Stackoverflow также может указывать на то, что он фактически используется в дикой природе.

  • " NCron - это небольшая библиотека для создания и развертывания запланированных фоновых заданий на платформе .NET." У него не так много функций, как Quartz.NET, и у него нет никакого тега в Stackoverflow, но автор (ваш по-настоящему) считает, что его API с низким коэффициентом трения облегчает работу с.

Создав службу расписания поверх NCron, вы можете запланировать CleanupJob для еженедельного выполнения с использованием одной строки кода:

service.Weekly().Run<CleanupJob>();

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

Ответ 2

В вашем методе "Start()" должен появиться поток, который просыпается с определенным интервалом, проверяет время и, если вы не достигли желаемого времени, возвращается спать.

Ответ 3

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

Ответ 4

Вы можете написать какой-то класс-оболочку для таймера, который принимает экземпляр DateTime. Затем выполните следующие действия:

  • Определите разницу между DateTime.Now и желаемым временем.
  • Если разница (в миллисекундах) больше максимально допустимого значения для свойства Timer.Interval, установите интервал в максимально допустимое значение (т.е. double.MaxValue или что-то еще) и запустите его.
  • Теперь, когда таймер истекает в первый раз, вы просто возвращаетесь к шагу 1.

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

Ответ 5

Используйте System.Threading.Timer:

    var timer = new System.Threading.Timer(delegate { }, // Pass here a delegate to the method
        null,
        TimeSpan.FromDays(7), // Execute Method after 7 days.
        TimeSpan.Zero);

Ответ 6

Вы можете использовать класс System.Threading.Timer, который предоставляет конструктор, принимающий интервал, выраженный как Int64, который должен быть достаточным для ваших нужд.

Теперь для других вещей:

  • Вы можете запускать/останавливать/настраивать программу с помощью класса Process (я действительно не получаю то, что вы называете "настраиваемыми командами" )
  • Вы не можете перезапустить или отключить локальный BIOS, используя собственные классы .NET. Перезагрузка/перезапуск возможен через Interop (вызов собственного API Windows из .NET), а планирование BIOS просто невозможно. Или, может быть, со специальной серверной материнской платой? Я не знаю..

Ответ 7

Класс System.Threading.Timer тоже имеет такое же ограничение (он бы выбрал ArgumentOutOfRangeException в соответствии с MSDN).

Кажется, что нет .Net Framework класса, изначально умелого, чтобы обойти верхнюю границу Int32.MaxValue миллисекунд.

public static class Scheduler
{
    private const long TimerGranularity = 100;

    static Scheduler()
     {
         ScheduleTimer = new Timer(Callback, null, Timeout.Infinite, Timeout.Infinite);
        Tasks = new SortedQueue<Task>();
     }

    private static void Callback(object state)
    {
        var first = Tasks.Peek();
        if(first.ExecuteAt<DateTime.Now)
        {
            Tasks.Dequeue();
            var executionThread = new Thread(() => first.Function());
            executionThread.Start();                
        }
    }

    private static Timer ScheduleTimer { get; set; }

    public static void Start()
    {
        ScheduleTimer.Change(0, TimerGranularity);
    }
    public static void Add(Task task)
    {
        Tasks.Enqueue(task);
    }

    public static SortedQueue<Task> Tasks { get; set; }
}

public class Task : IComparable<Task>
{
    public Func<Boolean> Function { get; set; }

    public DateTime ExecuteAt { get; set; }

    public int CompareTo(Task other)
    {
        return ExecuteAt.CompareTo(other.ExecuteAt);
    }
}

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

Задачи добавляются в очередь, способную выполнять сортировку вставки. Обратите внимание, что SortedQueue<T> не является типом .Net Framework, а представляет собой гипотетическую, легко-кодированную коллекцию, способную сортировать вставку на сопоставимом типе T.

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

Дополнительные усилия могут быть сделаны путем создания списка всех превзошедших задач (вместо первого); но я оставил его для ясности.

Ответ 8

Существует существует nu-get, называемый Quartz.NET.

Вы можете использовать его именно для этого.