2008-10-29 6 views
108

Мне нужно создать службу Windows, которая будет выполняться каждые N периодов времени.
Вопрос:
Какой контроль таймера следует использовать: System.Timers.Timer или System.Threading.Timer один? Это влияет на что-то?Лучший таймер для использования в службе Windows

Я спрашиваю, потому что я слышал много доказательств неверной работы System.Timers.Timer в службах windows.
Спасибо.

ответ

118

Оба System.Timers.Timer и System.Threading.Timer будут работать для обслуживания.

Таймеры, которых вы хотите избежать, - это System.Web.UI.Timer и System.Windows.Forms.Timer, которые соответственно предназначены для приложений ASP и WinForms. С их помощью служба загрузит дополнительную сборку, которая не нужна для типа используемого вами приложения.

Использование System.Timers.Timer как в следующем примере (также, убедитесь, что вы используете переменную уровня класса, чтобы предотвратить вывоз мусора, как указано в ответе Тима Робинсона):

using System; 
using System.Timers; 

public class Timer1 
{ 
    private static System.Timers.Timer aTimer; 

    public static void Main() 
    { 
     // Normally, the timer is declared at the class level, 
     // so that it stays in scope as long as it is needed. 
     // If the timer is declared in a long-running method, 
     // KeepAlive must be used to prevent the JIT compiler 
     // from allowing aggressive garbage collection to occur 
     // before the method ends. (See end of method.) 
     //System.Timers.Timer aTimer; 

     // Create a timer with a ten second interval. 
     aTimer = new System.Timers.Timer(10000); 

     // Hook up the Elapsed event for the timer. 
     aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent); 

     // Set the Interval to 2 seconds (2000 milliseconds). 
     aTimer.Interval = 2000; 
     aTimer.Enabled = true; 

     Console.WriteLine("Press the Enter key to exit the program."); 
     Console.ReadLine(); 

     // If the timer is declared in a long-running method, use 
     // KeepAlive to prevent garbage collection from occurring 
     // before the method ends. 
     //GC.KeepAlive(aTimer); 
    } 

    // Specify what you want to happen when the Elapsed event is 
    // raised. 
    private static void OnTimedEvent(object source, ElapsedEventArgs e) 
    { 
     Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime); 
    } 
} 

/* This code example produces output similar to the following: 

Press the Enter key to exit the program. 
The Elapsed event was raised at 5/20/2007 8:42:27 PM 
The Elapsed event was raised at 5/20/2007 8:42:29 PM 
The Elapsed event was raised at 5/20/2007 8:42:31 PM 
... 
*/ 

Если вы выбираете System.Threading.Timer, вы можете использовать следующим образом:

using System; 
using System.Threading; 

class TimerExample 
{ 
    static void Main() 
    { 
     AutoResetEvent autoEvent  = new AutoResetEvent(false); 
     StatusChecker statusChecker = new StatusChecker(10); 

     // Create the delegate that invokes methods for the timer. 
     TimerCallback timerDelegate = 
      new TimerCallback(statusChecker.CheckStatus); 

     // Create a timer that signals the delegate to invoke 
     // CheckStatus after one second, and every 1/4 second 
     // thereafter. 
     Console.WriteLine("{0} Creating timer.\n", 
      DateTime.Now.ToString("h:mm:ss.fff")); 
     Timer stateTimer = 
       new Timer(timerDelegate, autoEvent, 1000, 250); 

     // When autoEvent signals, change the period to every 
     // 1/2 second. 
     autoEvent.WaitOne(5000, false); 
     stateTimer.Change(0, 500); 
     Console.WriteLine("\nChanging period.\n"); 

     // When autoEvent signals the second time, dispose of 
     // the timer. 
     autoEvent.WaitOne(5000, false); 
     stateTimer.Dispose(); 
     Console.WriteLine("\nDestroying timer."); 
    } 
} 

class StatusChecker 
{ 
    int invokeCount, maxCount; 

    public StatusChecker(int count) 
    { 
     invokeCount = 0; 
     maxCount = count; 
    } 

    // This method is called by the timer delegate. 
    public void CheckStatus(Object stateInfo) 
    { 
     AutoResetEvent autoEvent = (AutoResetEvent)stateInfo; 
     Console.WriteLine("{0} Checking status {1,2}.", 
      DateTime.Now.ToString("h:mm:ss.fff"), 
      (++invokeCount).ToString()); 

     if(invokeCount == maxCount) 
     { 
      // Reset the counter and signal Main. 
      invokeCount = 0; 
      autoEvent.Set(); 
     } 
    } 
} 

Оба примера взяты на страницах MSDN.

+1

Почему вы предлагаете: GC.KeepAlive (aTimer) ;, aTimer - переменная экземпляра вправо, так, например, если это переменная экземпляра формы, всегда будет ссылка на нее, если есть форма, не так ли? – 2015-05-29 12:40:24

7

Любой должен работать нормально. Фактически System.Threading.Timer использует System.Timers.Timer внутренне.

Сказав это, легко использовать System.Timers.Timer. Если вы не храните объект Timer в переменной где-нибудь, тогда он может быть собран в мусор. Если это произойдет, ваш таймер больше не будет гореть. Вызовите метод Dispose, чтобы остановить таймер, или используйте класс System.Threading.Timer, который является немного более приятной оболочкой.

Какие проблемы вы видели до сих пор?

+0

заставляет меня задаться вопросом, почему телефон приложение для Windows может получить доступ только System.Threading.Timer. – Stonetip 2013-05-23 15:21:37

+0

Windows-телефоны, вероятно, имеют более легкую версию фреймворка, причем весь этот дополнительный код для использования обоих методов, вероятно, не нужен, поэтому он не включен. Я думаю, что ответ Ника, дает лучшую причину того, почему телефоны Windows не имеют доступа к `System.Timers.Timer`, потому что он не будет обрабатывать Исключения, брошенные на него. – Malachi 2013-07-10 20:35:24

37

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

Это общепринятая практика. Jon Galloway agrees with me. Or maybe its the other way around. В любом случае, факт заключается в том, что не рекомендуется создавать службу Windows для выполнения прерывистой задачи, запускаемой таймером.

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

-Jon Galloway, ASP.NET MVC менеджер программы сообщества, автор, неполный рабочий день супергероя

+26

Если ваша служба предназначена для работы весь день, возможно, услуга имеет смысл, а не запланированную задачу. Или, если служба может упростить администрирование и протоколирование для группы инфраструктуры, которая не так проницательна, как команда приложений. Однако вопрос о предположении, что услуга требуется, полностью действительна, и эти отрицательные оценки незаслуженно. +1 для обоих. – sfuqua 2010-07-30 14:22:30

+0

Задача, запланированная для Windows, чрезвычайно затруднительна. Услуги Windows намного надежнее. – 2012-12-09 08:31:52

2

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

Это будет:

  • Уменьшить сантехнические код, который повторяет поведение планировщика
  • обеспечивают большую гибкость с точки зрения поведения планирования (например, только запуск по выходным дням) со всей логикой планирования, абстрагированной от кода приложения
  • Используйте параметры командной строки для параметров без значения конфигурации конфигурации в co nfig файлы и т.д.
  • намного легче отладки/тестирования в процессе разработки
  • Разрешить пользователю поддержку для выполнения путем вызова консоль приложения непосредственно (например, полезные при поддержке ситуации)
1

Как уже говорилось как System.Threading.Timer и System.Timers.Timer будет работать. Большая разница между ними заключается в том, что System.Threading.Timer является оберткой вокруг другой.

System.Threading.Timer будет больше обработки исключений, а System.Timers.Timer поглотит все исключения.

Это дало мне большие проблемы в прошлом, поэтому я всегда буду использовать «System.Threading.Timer» и по-прежнему отлично справляюсь с вашими исключениями.

0

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

using System; 
using System.ServiceProcess; 
using System.Threading; 

    public partial class TimerExampleService : ServiceBase 
    { 
     private AutoResetEvent AutoEventInstance { get; set; } 
     private StatusChecker StatusCheckerInstance { get; set; } 
     private Timer StateTimer { get; set; } 
     public int TimerInterval { get; set; } 

     public CaseIndexingService() 
     { 
      InitializeComponent(); 
      TimerInterval = 300000; 
     } 

     protected override void OnStart(string[] args) 
     { 
      AutoEventInstance = new AutoResetEvent(false); 
      StatusCheckerInstance = new StatusChecker(); 

      // Create the delegate that invokes methods for the timer. 
      TimerCallback timerDelegate = 
       new TimerCallback(StatusCheckerInstance.CheckStatus); 

      // Create a timer that signals the delegate to invoke 
      // 1.CheckStatus immediately, 
      // 2.Wait until the job is finished, 
      // 3.then wait 5 minutes before executing again. 
      // 4.Repeat from point 2. 
      Console.WriteLine("{0} Creating timer.\n", 
       DateTime.Now.ToString("h:mm:ss.fff")); 
      //Start Immediately but don't run again. 
      StateTimer = new Timer(timerDelegate, AutoEventInstance, 0, Timeout.Infinite); 
      while (StateTimer != null) 
      { 
       //Wait until the job is done 
       AutoEventInstance.WaitOne(); 
       //Wait for 5 minutes before starting the job again. 
       StateTimer.Change(TimerInterval, Timeout.Infinite); 
      } 
      //If the Job somehow takes longer than 5 minutes to complete then it wont matter because we will always wait another 5 minutes before running again. 
     } 

     protected override void OnStop() 
     { 
      StateTimer.Dispose(); 
     } 
    } 

    class StatusChecker 
     { 

      public StatusChecker() 
      { 
      } 

      // This method is called by the timer delegate. 
      public void CheckStatus(Object stateInfo) 
      { 
       AutoResetEvent autoEvent = (AutoResetEvent)stateInfo; 
       Console.WriteLine("{0} Start Checking status.", 
        DateTime.Now.ToString("h:mm:ss.fff")); 
       //This job takes time to run. For example purposes, I put a delay in here. 
       int milliseconds = 5000; 
       Thread.Sleep(milliseconds); 
       //Job is now done running and the timer can now be reset to wait for the next interval 
       Console.WriteLine("{0} Done Checking status.", 
        DateTime.Now.ToString("h:mm:ss.fff")); 
       autoEvent.Set(); 
      } 
     } 

 Смежные вопросы

  • Нет связанных вопросов^_^