El mejor temporizador para usar en un servicio de Windows

Resuelto asked hace 16 años • 6 respuestas

Necesito crear algún servicio de Windows que se ejecute cada N período de tiempo.
La pregunta es: ¿
Qué control de temporizador debo usar: System.Timers.Timero System.Threading.Timeruno? ¿Influye en algo?

Lo pregunto porque escuché muchas evidencias de que System.Timers.Timerlos servicios de Windows no funcionan correctamente.
Gracias.

 avatar Oct 29 '08 20:10
Aceptado

Ambos System.Timers.Timery System.Threading.Timertrabajarán para los servicios.

Los temporizadores que desea evitar son System.Web.UI.Timery System.Windows.Forms.Timer, que son respectivamente para aplicaciones ASP y WinForms. Su uso hará que el servicio cargue un ensamblaje adicional que realmente no es necesario para el tipo de aplicación que está creando.

Úselo System.Timers.Timercomo en el siguiente ejemplo (también asegúrese de usar 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 lo eliges System.Threading.Timer, puedes utilizar lo siguiente:

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.

Andrew Moore avatar Oct 29 '2008 13:10 Andrew Moore

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

Esta es la mejor práctica comúnmente aceptada. Jon Galloway está de acuerdo conmigo. O tal vez sea al revés. De cualquier manera, el hecho es que no es una buena práctica crear un servicio de Windows para realizar una tarea intermitente ejecutada con un temporizador.

"Si está escribiendo un servicio de Windows que ejecuta un temporizador, debe reevaluar su solución".

–Jon Galloway, director del programa comunitario ASP.NET MVC, autor, superhéroe a tiempo parcial

 avatar Oct 29 '2008 13:10

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

Dicho esto, es fácil hacer un mal uso de System.Timers.Timer. Si no almacena el objeto Timer en una variable en algún lugar, es probable que se recolecte basura. 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 un poco mejor.

¿Qué problemas has visto hasta ahora?

Tim Robinson avatar Oct 29 '2008 13:10 Tim Robinson

Estoy de acuerdo con el comentario anterior en que sería mejor considerar un enfoque diferente. Mi sugerencia sería escribir una aplicación de consola y utilizar el programador de Windows:

Esta voluntad:

  • Reducir el código de plomería que replica el comportamiento del programador
  • Proporcionar mayor flexibilidad en términos de comportamiento de programación (por ejemplo, solo se ejecuta los fines de semana) con toda la lógica de programación abstraída del código de la aplicación.
  • Utilice los argumentos de la línea de comando para los parámetros sin tener que configurar valores de configuración en archivos de configuración, etc.
  • Mucho más fácil de depurar/probar durante el desarrollo
  • Permitir que un usuario de soporte ejecute invocando la aplicación de consola directamente (por ejemplo, útil durante situaciones de soporte).
user32378 avatar Oct 29 '2008 14:10 user32378

Como ya se dijo, ambos System.Threading.Timerfuncionarán System.Timers.Timer. La gran diferencia entre los dos es que System.Threading.Timerhay una envoltura alrededor del otro.

System.Threading.Timertendrá más manejo de excepciones mientras System.Timers.Timertragará todas las excepciones.

Esto me dio grandes problemas en el pasado, por lo que siempre usaba 'System.Threading.Timer' y aún así manejaba muy bien las excepciones.

Nick N. avatar May 15 '2013 13:05 Nick N.