С# - альтернатива 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 для обработки задач планирования, и сначала это кажется хорошим решением, но я бы как услышать о тех, кто работал с ним.
Ответы
Ответ 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.
Вы можете использовать его именно для этого.