none
Вызов процедуры каждый час в начале часа RRS feed

  • Вопрос

  • Суть: процедура должна вызываться каждый час в 0 минут, 0 секунд (или какие-то другие, но заранее определенные). Предполагается такое решение: определить текущее время, вычислить сколько времени до ближайшего события. Запустить таймер на время до ближайшего события.Затем в цикле вызывать процедуру, запуская после обращения таймер на час. Интересует: есть ли альтернатива?
    15 января 2011 г. 1:14
    Отвечающий

Ответы

  • Я делал таким же способом, разве что повторный таймер запускал не на час, а на вновь вычисленное время (нужно учитывать время, затрачиваемое на выполнение метода, который вызывает таймер).

    Альтернативы есть, но они будут менее эффективными.

    • Помечено в качестве ответа PetrishkoMVP, Editor 15 января 2011 г. 20:20
    15 января 2011 г. 10:11
  • Я как то для этих целей целый класс написал, вот, может пригодится:

    public delegate void TimerReachedEventHandler(TimerReachedEventArgs e);
    
      public static class Alarm
      {
        public static event TimerReachedEventHandler TimerReached;
    
        private static ArrayList Timers = new ArrayList();
    
        public class Timer
        {
          public DateTime LastAlert;
          public TimeSpan Period;
          public DateTime Time;
          public int ID;
        }
    
        private static Thread AThread = null;
    
        public static bool Start()
        {
          AThread = new Thread(AlarmThread);
          try
          {
            AThread.Start();
            return true;
          }
          catch
          {
            return false;
          }
        }
    
        public static bool Stop()
        {
          try
          {
            AThread.Abort();
            return true;
          }
          catch
          {
            return false;
          }
        }
    
        private static void AlarmThread()
        {
          DateTime Now;
          while (true)
          {
            Now = DateTime.Now;
            if (Timers != null)
            {
              foreach (Timer Tm in Timers)
              {
                if (Tm.Time.ToLongTimeString() == Now.ToLongTimeString() & Now - Tm.LastAlert >= new TimeSpan(0, 0, 1))
                {
                  TimerReachedEventArgs e = new TimerReachedEventArgs(Tm.ID, Tm.Time);
                  SendMessageTimerReached(e);
                  Tm.LastAlert = Now;
                  if (Tm.Period != new TimeSpan(0, 0, 0))
                  {
                    Tm.Time += Tm.Period;
                  }
                }
              }
            }
            Thread.Sleep(250);
          }
        }
    
        public static bool AddNewTimer(int ID, DateTime Time)
        {
          AddNewTimer(ID, Time, new TimeSpan(0, 0, 0));
          return true;
        }
    
        public static bool AddNewTimer(int ID, DateTime Time, TimeSpan Period)
        {
          Timer AddingTimer = new Timer();
          AddingTimer.ID = ID;
          AddingTimer.Time = Time;
          AddingTimer.Period = Period;
          AddingTimer.LastAlert = DateTime.Now;
          Timers.Add(AddingTimer);
          return true;
        }
    
        public static bool DeleteTimer(int ID)
        {
          bool Deleted = false;
          foreach (Timer Tm in Timers)
          {
            if (Tm.ID == ID)
            {
              Timers.Remove(Tm);
              Deleted = true;
            }
          }
          return Deleted;
        }
    
        public static bool DeleteTimer(DateTime Time)
        {
          bool Deleted = false;
          foreach (Timer Tm in Timers)
          {
            if (Tm.Time == Time)
            {
              Timers.Remove(Tm);
              Deleted = true;
            }
          }
          return Deleted;
        }
    
        private static void SendMessageTimerReached(TimerReachedEventArgs e)
        {
          if (TimerReached != null)
            TimerReached(e);
        }
      }
    
      public class TimerReachedEventArgs : EventArgs
      {
        private Alarm.Timer CTimer = new Alarm.Timer();
    
        public Alarm.Timer GetTimer
        {
          get { return CTimer; }
        }
        public DateTime GetTime
        {
          get { return CTimer.Time; }
        }
        public int GetID
        {
          get { return CTimer.ID; }
        }
    
        public TimerReachedEventArgs(int ID, DateTime Time)
        {
          this.CTimer.ID = ID;
          this.CTimer.Time = Time;
        }
      }

    Вот пример использования:

    static void Main(string[] args)
      {
       Alarm.TimerReached += new TimerReachedEventHandler(Alarm_TimerReached);
       Alarm.AddNewTimer(1, Convert.ToDateTime("21:13"));
       Alarm.AddNewTimer(2, Convert.ToDateTime("21:13"), new TimeSpan(0, 0, 10));
       Alarm.AddNewTimer(3, Convert.ToDateTime("21:13"), new TimeSpan(0, 0, 5));
       if (Alarm.Start())
        Console.WriteLine("Таймер запущен.");
       Console.ReadKey();
       if (Alarm.Stop())
        Console.WriteLine("Таймер остановлен.");
       Console.ReadKey();
      }
    
      static void Alarm_TimerReached(TimerReachedEventArgs e)
      {
       Console.WriteLine("Сработал таймер №{0}. Время {1}.", e.GetID, e.GetTime.ToLongTimeString());
      }
    • Помечено в качестве ответа PetrishkoMVP, Editor 15 января 2011 г. 20:41
    15 января 2011 г. 13:23

Все ответы

  • Я делал таким же способом, разве что повторный таймер запускал не на час, а на вновь вычисленное время (нужно учитывать время, затрачиваемое на выполнение метода, который вызывает таймер).

    Альтернативы есть, но они будут менее эффективными.

    • Помечено в качестве ответа PetrishkoMVP, Editor 15 января 2011 г. 20:20
    15 января 2011 г. 10:11
  • А чем плох стандартный планировщик заданий ? http://www.ixbt.com/soft/ms-task-scheduler.shtml
    15 января 2011 г. 11:57
  • Он не плох, но он запускает процессы , а вопрос о методах (процедурах).
    15 января 2011 г. 12:47
  • Если единственная задача приложения - вызывать процедуру в 0:00, то проще само приложение запускать раз в сутки. 
    15 января 2011 г. 12:53
  • Поправка: раз в час.

    Если задача именно такая, то да, лучше запускать приложение из планировщика. Более того, оно может запускаться даже если ни один пользователь еще не вошел в систему (конечно, при условии, что компьютер включен).

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

    Резюмируя: мы ничего не знаем о задаче, а согласно точной формулировке вопроса планировщик задач не подходит.

    15 января 2011 г. 13:02
  • Я как то для этих целей целый класс написал, вот, может пригодится:

    public delegate void TimerReachedEventHandler(TimerReachedEventArgs e);
    
      public static class Alarm
      {
        public static event TimerReachedEventHandler TimerReached;
    
        private static ArrayList Timers = new ArrayList();
    
        public class Timer
        {
          public DateTime LastAlert;
          public TimeSpan Period;
          public DateTime Time;
          public int ID;
        }
    
        private static Thread AThread = null;
    
        public static bool Start()
        {
          AThread = new Thread(AlarmThread);
          try
          {
            AThread.Start();
            return true;
          }
          catch
          {
            return false;
          }
        }
    
        public static bool Stop()
        {
          try
          {
            AThread.Abort();
            return true;
          }
          catch
          {
            return false;
          }
        }
    
        private static void AlarmThread()
        {
          DateTime Now;
          while (true)
          {
            Now = DateTime.Now;
            if (Timers != null)
            {
              foreach (Timer Tm in Timers)
              {
                if (Tm.Time.ToLongTimeString() == Now.ToLongTimeString() & Now - Tm.LastAlert >= new TimeSpan(0, 0, 1))
                {
                  TimerReachedEventArgs e = new TimerReachedEventArgs(Tm.ID, Tm.Time);
                  SendMessageTimerReached(e);
                  Tm.LastAlert = Now;
                  if (Tm.Period != new TimeSpan(0, 0, 0))
                  {
                    Tm.Time += Tm.Period;
                  }
                }
              }
            }
            Thread.Sleep(250);
          }
        }
    
        public static bool AddNewTimer(int ID, DateTime Time)
        {
          AddNewTimer(ID, Time, new TimeSpan(0, 0, 0));
          return true;
        }
    
        public static bool AddNewTimer(int ID, DateTime Time, TimeSpan Period)
        {
          Timer AddingTimer = new Timer();
          AddingTimer.ID = ID;
          AddingTimer.Time = Time;
          AddingTimer.Period = Period;
          AddingTimer.LastAlert = DateTime.Now;
          Timers.Add(AddingTimer);
          return true;
        }
    
        public static bool DeleteTimer(int ID)
        {
          bool Deleted = false;
          foreach (Timer Tm in Timers)
          {
            if (Tm.ID == ID)
            {
              Timers.Remove(Tm);
              Deleted = true;
            }
          }
          return Deleted;
        }
    
        public static bool DeleteTimer(DateTime Time)
        {
          bool Deleted = false;
          foreach (Timer Tm in Timers)
          {
            if (Tm.Time == Time)
            {
              Timers.Remove(Tm);
              Deleted = true;
            }
          }
          return Deleted;
        }
    
        private static void SendMessageTimerReached(TimerReachedEventArgs e)
        {
          if (TimerReached != null)
            TimerReached(e);
        }
      }
    
      public class TimerReachedEventArgs : EventArgs
      {
        private Alarm.Timer CTimer = new Alarm.Timer();
    
        public Alarm.Timer GetTimer
        {
          get { return CTimer; }
        }
        public DateTime GetTime
        {
          get { return CTimer.Time; }
        }
        public int GetID
        {
          get { return CTimer.ID; }
        }
    
        public TimerReachedEventArgs(int ID, DateTime Time)
        {
          this.CTimer.ID = ID;
          this.CTimer.Time = Time;
        }
      }

    Вот пример использования:

    static void Main(string[] args)
      {
       Alarm.TimerReached += new TimerReachedEventHandler(Alarm_TimerReached);
       Alarm.AddNewTimer(1, Convert.ToDateTime("21:13"));
       Alarm.AddNewTimer(2, Convert.ToDateTime("21:13"), new TimeSpan(0, 0, 10));
       Alarm.AddNewTimer(3, Convert.ToDateTime("21:13"), new TimeSpan(0, 0, 5));
       if (Alarm.Start())
        Console.WriteLine("Таймер запущен.");
       Console.ReadKey();
       if (Alarm.Stop())
        Console.WriteLine("Таймер остановлен.");
       Console.ReadKey();
      }
    
      static void Alarm_TimerReached(TimerReachedEventArgs e)
      {
       Console.WriteLine("Сработал таймер №{0}. Время {1}.", e.GetID, e.GetTime.ToLongTimeString());
      }
    • Помечено в качестве ответа PetrishkoMVP, Editor 15 января 2011 г. 20:41
    15 января 2011 г. 13:23
  • Отличный класс, но я, все же, порекомендовал бы воспользоваться таймером из System.Threading.Timer вместо блокировки потока операцией Thread.Sleep(250). Подробнее обо всех типах таймеров в .NET можно прочитать в этой статье (к сожалению, есть только английская версия этой статьи).
    15 января 2011 г. 13:31
  • Не знаю, братцы! По-моему, тема высосана из пальца. Процесс, или процедура, или метод... Какая разница? Нужно с заданной периодичностью закачать и/или обработать данные, или чтобы система свистнула, причем чтобы даже спросонья, или блыманула еще как-нибудь, ну что еще может быть?.. Все эти функции легко программируются и исполняются планировщиком, просто и красиво - отработала и уснула, выгрузилась, забылась... И не нужно ничего плодить...
    15 января 2011 г. 15:08
  • Ну, не скажите. Как вам такая задача: накопление данных в буфере в памяти в фоновом режиме и сброс их на диск в начале каждого часа? А если еще потребуется пользовательский интерфейс для настройки расписания? Для такой задачи использование планировщика будет чересчур.
    15 января 2011 г. 15:19
  • А если нужно каждую минуту закачивать, каждый час - проверять, каждый день подсчитывать, каждую неделю составлять итог для уважамого начальства, раз в квартал..., раз в год... Попробуйте-ка кто-нибудь поиспользуйте тот класс, которым мы все только что полубовались, и что у вас получится... Вот уж этот велосипед изобретать не надо - себе дороже...
    15 января 2011 г. 15:20
  • Если данные поступают ежесекундно или чаще, то это совсем другая тема - это уже постоянно висящий сканер или приемник данных, а совсем не периодически запускаемый процесс(процедура, метод).
    15 января 2011 г. 15:29
  • Резюмируя: мы ничего не знаем о задаче, а согласно точной формулировке вопроса планировщик задач не подходит.

    Согласно точной формулировке задачи - никто не знает, подходит ли планировщик. Топикастер спрашивал об альтернативах - вынести код, запускаемый по расписанию, в отдельное приложение - вполне так альтернатива. Ничем не менее предпочтительная в случае редактирования расписания из UI. Например, тот же стандартный Defrag в 7-ке позволяет редактировать свое расписание в родном UI, но использует Task Scheduler. 

    И даже без планировщика альтернатив хватает - например, использовать таймер c коротким интервалом, и в обработчике проверять, наступил ли час X (что-то вроде примера JusteG).

    15 января 2011 г. 16:09
  • О редактировании расписания речь не шла. Тема звучит абсолютно просто - "Вызов процедуры каждый час в начале часа". Нет, я не против своих собственных программ-пускачей, вопрос в другом - зачем, если есть готовый? А вообще есть проблемы поинтересней. Всем спасибо!
    15 января 2011 г. 16:53
  • А если нужно каждую минуту закачивать, каждый час - проверять, каждый день подсчитывать, каждую неделю составлять итог для уважамого начальства, раз в квартал..., раз в год... Попробуйте-ка кто-нибудь поиспользуйте тот класс, которым мы все только что полубовались, и что у вас получится... Вот уж этот велосипед изобретать не надо - себе дороже...

    Что может быть проще?

     

    static void Main(string[] args)
      {
       Alarm.TimerReached += new TimerReachedEventHandler(Alarm_TimerReached);
       Alarm.AddNewTimer(1, Convert.ToDateTime("00:00"), new TimeSpan(0, 1, 0));
       Alarm.AddNewTimer(2, Convert.ToDateTime("00:00"), new TimeSpan(1, 0, 0));
       Alarm.AddNewTimer(3, Convert.ToDateTime("00:00"), new TimeSpan(1, 0, 0, 0, 0));
       Alarm.AddNewTimer(4, Convert.ToDateTime("00:00"), new TimeSpan(7, 0, 0, 0, 0));
       Alarm.Start();
      }
    
      static void Alarm_TimerReached(TimerReachedEventArgs e)
      {
       switch (e.GetID)
       {
        case 1:
         Download();
         break;
        case 2:
         Check();
         break;
        case 3:
         Count();
         break;
        case 4:
         Report();
         break;
       }
      }

     

    На порядок легче чем любым другим способом...

     

    15 января 2011 г. 18:40
  • Если Вы обратите внимание, я не предлагал пользоваться им.
    15 января 2011 г. 18:50
  • Периодически запускаемый метод - сброс данных на диск. Это один метод, а не все приложение.

    Если пользоваться для этого планировщиком, придется реализовывать костыли в виде межпроцессного взаимодействия.

    15 января 2011 г. 18:52
  • Все-таки объект таймера работает на уровне, если не ошибаюсь, ядра, поэтому его использование будет гораздо более эффективным. Также в этом случае отсутствует дискретность в 250 мс. А насчет простоты кода... вот пример вызова такого таймера:

    System.Threading.Timer timer = new System.Threading.Timer(new TimerCallback(timerCallback), null, 0, 1000 * 60 * 60);
    
    

    15 января 2011 г. 19:07
  • Что тут скажешь, каждый хвалит свою корову. На счет таймеров интересная тема, надо будет как нить заморочится. А что каcается вариантов решения вопроса, считаю что топикастеру хватит вариантов из уже предложенных. Для общения предлагаю ICQ или Skype.
    15 января 2011 г. 19:13
  • Хорошо, согласен, ответ можно сформулировать следующим образом:

    Если между запусками этой процедуры процесс должен работать, следует использовать таймеры.

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

    А другие альтернативы, вроде постоянного опроса времени, Thread.Sleep на определенное время или написания драйвера-планировщика, как я и написал в первом ответе, будут менее эффективными по потребляемым ресурсам или трудозатратам.

    15 января 2011 г. 19:19
  • Если единственная задача приложения - вызывать процедуру в 0:00, то проще само приложение запускать раз в сутки. 
    Задача не единственная, это одна из задач. Вполне возможно что на следующем этапе развития программы нужно будет запускать потоки по расписанию с заданной периодичностью (такого же рода - каждый час в 30 минуту и т.п.). При этом в фоне выполняется другой функционал программы (то есть так, как в варианте описываемом Алексеем Митевым)
    Процесс, или процедура, или метод... Какая разница? . 

    Если в фоне программы выполняется другой функционал и всё вместе это должно быть единым приложением, то есть разница.

    Если так и останется с одна процедура выполняемая по расписанию, то всё так и оставлю. С той корректировкой, что таймер запускается на вновь вычесленное время. Думал об этом и сомнения были насчёт рассинхронизации.
    Если дойдёт до потоков, тогда задумаюсь о классе, как пишет JusteG.

    15 января 2011 г. 20:41
    Отвечающий