2008-10-29 10 views
108

Necesito crear un servicio de Windows que se ejecutará cada N período de tiempo.
La pregunta es:
¿Qué control de temporizador debo usar: System.Timers.Timer o System.Threading.Timer uno? ¿Influye en algo?Mejor temporizador para usar en un servicio de Windows

Lo estoy preguntando porque escuché muchas evidencias del trabajo no correcto de System.Timers.Timer en los servicios de Windows.
Gracias.

Respuesta

118

Tanto System.Timers.Timer como System.Threading.Timer funcionarán para los servicios. Los temporizadores que desea evitar son System.Web.UI.Timer y System.Windows.Forms.Timer, que son respectivamente para aplicaciones ASP y WinForms. El uso de estos hará que el servicio cargue un ensamblaje adicional que no es realmente necesario para el tipo de aplicación que está construyendo.

Uso System.Timers.Timer como el siguiente ejemplo (también, asegúrese de que utiliza una variable de nivel de clase para evitar la recolección de basura, como se indica en la respuesta de Tim Robinson):

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 
... 
*/ 

Si elige System.Threading.Timer, puede utilizar de la siguiente manera:

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(); 
     } 
    } 
} 

Ambos ejemplos provienen de las páginas de MSDN.

+1

¿Por qué sugieres: GC.KeepAlive (aTimer) ;, aTimer es una variable de instancia correcta, por lo que, por ejemplo, si es una variable de instancia de la forma, siempre habrá referencia a ella siempre que exista forma, ¿no es así? –

7

Cualquiera de los dos debería funcionar bien. De hecho, System.Threading.Timer utiliza System.Timers.Timer internamente.

Habiendo dicho eso, es fácil hacer un mal uso de System.Timers.Timer. Si no almacena el objeto Timer en una variable en algún lugar, entonces es probable que sea basura recolectada. Si eso sucede, su temporizador ya no se disparará. Llame al método Dispose para detener el temporizador, o use la clase System.Threading.Timer, que es un contenedor ligeramente más agradable.

¿Qué problemas ha visto hasta ahora?

+0

hace que me pregunte por qué una aplicación Windows Phone sólo se puede acceder System.Threading.Timer. – Stonetip

+0

los teléfonos con Windows probablemente tengan una versión más ligera del marco, probablemente no sea necesario tener todo ese código adicional para poder usar ambos métodos, por lo que no está incluido. Creo que la respuesta de Nick da una mejor razón de por qué los teléfonos Windows no tienen acceso a 'System.Timers.Timer' porque no manejará las Excepciones que se le envíen. – Malachi

37

No utilice un servicio para esto. Crea una aplicación normal y crea una tarea programada para ejecutarla.

Esta es la mejor práctica común. Jon Galloway agrees with me. Or maybe its the other way around. De cualquier forma, el hecho es que no es una buena práctica crear un servicio de Windows para realizar una tarea intermitente ejecutando un temporizador.

"Si está escribiendo un Servicio de Windows que ejecuta un temporizador, debe volver a evaluar su solución."

-Jon Galloway, ASP.NET MVC director del programa de la comunidad, autor, tiempo parcial de superhéroes

+26

Si su servicio está destinado a ejecutarse todo el día, quizás un servicio tenga sentido en lugar de una tarea programada. O bien, tener un servicio puede simplificar la administración y el registro para un grupo de infraestructura que no es tan inteligente como el equipo de aplicaciones. Sin embargo, cuestionar la suposición de que se requiere un servicio es completamente válido, y estas calificaciones negativas no se merecen. +1 a ambos. – sfuqua

+0

La tarea programada de Windows tiene muchas fallas. Los servicios de Windows son mucho más confiables. –

+2

@ m.r. Disparates. Las tareas programadas son una parte central del sistema operativo. Por favor, mantenga su FUD a usted mismo. – Will

2

me acuerdo con el comentario anterior que podría ser mejor considerar un enfoque diferente.Mi sugerir que sería escribir una aplicación de consola y utilizar el Programador de Windows:

Esta voluntad:

  • Reducir código de plomería que replica el comportamiento del planificador
  • proporcionar una mayor flexibilidad en términos de comportamientos de programación (por ejemplo, sólo se ejecuta los fines de semana) con toda la lógica de programación abstraída del código de aplicación
  • Utilice los argumentos de la línea de comando para los parámetros sin tener que valores de configuración de configuración en co nfig archivos etc
  • Mucho más fácil de depurar/prueba durante el desarrollo
  • permitir a un usuario el apoyo para ejecutar invocando la aplicación de consola directamente (por ejemplo, útiles durante el soporte situaciones)
+3

¿Pero esto requiere un usuario conectado? Entonces, el servicio es mejor si funciona las 24 horas en un servidor. –

1

Como ya se ha indicado, tanto System.Threading.Timer y System.Timers.Timer va a funcionar. La gran diferencia entre los dos es que System.Threading.Timer es un envoltorio alrededor del otro.

System.Threading.Timer tendrán más control de excepciones, mientras que System.Timers.Timer se trague todas las excepciones.

Esto me dio grandes problemas en el pasado, así que siempre usaría 'System.Threading.Timer' y aún manejaría muy bien sus excepciones.

+0

Vea sus propias excepciones y no se encontraría con esos problemas ... –

0

Sé que este hilo es un poco viejo, pero fue útil para un escenario específico que tuve y pensé que valía la pena señalar que hay otra razón por la cual System.Threading.Timer podría ser un buen enfoque. Cuando tiene que ejecutar periódicamente un trabajo que puede llevar mucho tiempo y desea asegurarse de que todo el período de espera se utiliza entre trabajos o si no desea que el trabajo se ejecute nuevamente antes de que el trabajo anterior haya finalizado en el caso donde el trabajo lleva más tiempo que el período del temporizador. Se podría utilizar lo siguiente:

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(); 
      } 
     } 
Cuestiones relacionadas