2009-09-30 15 views
21

Estoy escribiendo un servicio de Windows que una vez que se inicia se ejecutará cada X horas. El proceso que completa es bastante intensivo, por lo que quiero utilizar un trabajador de segundo plano. Estoy usando un archivo de configuración para almacenar tanto las horas entre las ejecuciones como la última vez que se ejecutó el servicio.Servicio de Windows que se ejecuta periódicamente

No estoy seguro de cuál es la mejor manera de hacerlo, es decir, quiero que el servicio permanezca inactivo utilizando la menor cantidad de recursos posible y, cuando se ejecuta, debe ejecutarse en segundo plano, informar qué lo hizo, y luego volver al modo inactivo.

He pensado en usar 2 trabajadores de fondo. El primer trabajador sería una variable privada local para el servicio que se ejecuta algo como esto:

while (true) 
{ 
     //create new background worker and run 

     Thread.Sleep(Settings.Default.SleepTimeHours * 3600000); 
} 

con un trabajador secundario que se crea cada iteración del bucle, y destruido cuando esté terminado. Para admitir la cancelación, creo que debería tener una instancia local del segundo trabajador disponible en el servicio, pero será nulo si el proceso no se está ejecutando actualmente. Cuando el trabajador secundario termina, enviará mi informe, establecerá el último tiempo de ejecución en el archivo de configuración y luego eliminará al trabajador y establecerá la referencia en nulo.

Me pregunto si hay una mejor manera de hacer esto o una mejor práctica.

Gracias

+0

Para aquellos que sugiere utilizar el programador de tareas - Me propuso que como una opción, pero el arquitecto quiere que sea un servicio de Windows. – Josh

+2

@Broken Bokken: Lo mejor de ambos mundos: http://www.genericgeek.com/Enable-a-service-from-command-line;) – JustLoren

Respuesta

33

Normalmente uso un temporizador, luego lo detengo cuando el proceso comienza a ejecutarse.

Aquí hay un artículo que explica how to do it.

+1

+1: buen enlace de tutorial – Zenuka

+0

Ese artículo era un buen enlace. Gracias. – Josh

+0

Sin embargo, creo que aún se bloqueará la secuencia de eventos del temporizador, lo que podría provocar que el servicio deje de responder a Inicio(), Detener(), Restablecer() etc. Los servicios deben poder responder a estos en 30 segundos, por lo que el enfoque sin bloqueo roscado sería mejor. – Ian

1

En mi empresa, que se basan en el programador de tareas de Windows para iniciar el servicio, y luego tener el servicio cerrarse a sí mismo. Esto asegura que nuestro servicio siempre se ejecute en el momento correcto y el sistema de programación permitirá informar de éxito/falla, etc.

+0

No se ampliará tan bien como algo así como Quartz .. –

+0

No estoy seguro de lo que significa escalar. Tenemos aproximadamente 15 servicios que funcionan todas las noches, y alrededor de 30 que se ejecutan el fin de semana. ¿Cuántos servicios crees que necesitaremos ejecutar? lol – JustLoren

1

Use un temporizador para hacer esto.

13

No es una buena idea, ya que bloquea el hilo durante el período completo de "SleepTimeHours" y no podrá detener el servicio mientras tanto.

Puede realizar este servicio para que duerma, p. 5 segundos y luego verifique si es hora de volver al trabajo, y si no, duerma otros 5 segundos (eso le daría la capacidad de respuesta necesaria, si necesita detener el servicio).

O: puede que sea mejor que simplemente escriba una aplicación de consola que se puede programar con la función "tareas programadas" de Windows para que se ejecute cada x horas. De esta manera, usted no será el bloqueo o el uso de cualquier recurso del sistema si su aplicación no está haciendo nada ......

Marc

+3

Buen punto para no dormir las X horas completas. Gracias, Marc. – Josh

+1

En lugar de while (true) puede agregar un tiempo (_isPolling) luego, cuando detiene el servicio, se detendrá y el hilo terminará cuando eventualmente se despierte :) –

+0

Estaba a punto de sugerir la solución de tarea programada en lugar de un servicio . – Philippe

10

Considere un planificador de tareas como Quartz.Net.

http://quartznet.sourceforge.net/

+0

Solía ​​trabajar con temporizadores, pero para un nuevo proyecto ahora uso Quartz.net y, francamente, su CronTrigger, que le permite activar un trabajo con cadenas cron, es demasiado bueno. Gracias por su sugerencia aquí! – Marc

2

que realmente no quieren usar trabajador de fondo. Los trabajadores en segundo plano se utilizan cuando tiene algo en primer plano (como la interfaz de usuario) y también quiere que se haga algo más en el fondo. En su caso no hay procesamiento en primer plano, todo lo que tiene es un trabajo único que necesita ejecutar de vez en cuando.

Además, no se moleste con el negocio para dormir, etc. En su lugar, use un marco de programación, como Quartz.NET para activarlo de vez en cuando. De esta forma, cuando se inicie el servicio, se inicializará el programador y no se hará absolutamente nada hasta que el programador se active. Cuando el programador se despierta, llamará a un método de un objeto al que le diga que llame cuando lo inicialice.

De esta forma, el servicio apenas consumirá CPU cuando esté inactivo y funcionará a pleno rendimiento cuando sea necesario.

7

¿Qué tal algo de la misma familia:

public class LongRunningService : ServiceBase 
{ 
    System.Threading.Thread processThread; 
    System.Timers.Timer timer; 
    private Boolean Cancel; 

    protected override void OnStart(string[] args) 
    { 
     timer = new Timer(Settings.Default.SleepTimeHours * 3600000); 
     timer.Elapsed += new ElapsedEventHandler(timer_Tick); 
     timer.Start(); 

     Cancel = false; 
    } 

    protected override void OnContinue() 
    { 
     timer.Start(); 
    } 

    protected override void OnPause() 
    { 
     timer.Stop(); 
    } 

    protected override void OnStop() 
    { 
     if (processThread.ThreadState == System.Threading.ThreadState.Running) 
     { 
      Cancel = true; 
      // Give thread a chance to stop 
      processThread.Join(500); 
      processThread.Abort(); 
     } 
    } 

    void timer_Tick(object sender, EventArgs e) 
    { 
     processThread = new System.Threading.Thread(new ThreadStart(DoWork)); 
     processThread.Start(); 
    } 

    private void DoWork() 
    { 
     try 
     { 
      while (!Cancel) 
      { 

      if (Cancel) { return; } 
      // Do General Work 

      System.Threading.Thread.BeginCriticalRegion(); 
      { 
       // Do work that should not be aborted in here. 
      } 
      System.Threading.Thread.EndCriticalRegion(); 
      } 

     } 
     catch (System.Threading.ThreadAbortException tae) 
     { 
      // Clean up correctly to leave program in stable state. 
     } 
    } 
} 
+0

Sugeriría usar la clase System.Timers.Timer en lugar del System.Windows.Forms.Timer. Forms.Timer requerirá que se cargue System.Windows.Forms.dll, que no es algo que generalmente se necesita para los servicios de Windows. –

+0

Estoy de acuerdo en realidad. Actualizado el ejemplo – Ian

+0

¿Cuál es el propósito del tiempo (! Cancel) {} en DoWork() y cómo la ejecución pasará alguna vez por debajo de if (Cancelar) {return;}? ¿Tal vez la llave final del tiempo debería estar cerca del final? – pug

1

tuve la misma discusión con colegas hace 1 hora! Fui con la opción while (_isPolling), porque necesitaba que el trabajo sucediera sincrónicamente. No quería que el mismo trabajo fuera recogido por otro hilo (el enfoque del temporizador), y la implementación de bloqueo adicional para eso parecía un desperdicio.

4

Utilice un enfoque basado en el enfoque System.Threading.WaitHandle.

using System.Threading; 
private Thread _thread; 
private ManualResetEvent _shutdownEvent = new ManualResetEvent(false); 
private ManualResetEvent _scheduleEvent = new ManualResetEvent(false); 
private System.Timers.Timer _scheduleTimer = new System.Timers.Timer(); 
protected override void OnStart(string[] args) 
{ 
    // Configure the timer. 
    _scheduleTimer.AutoReset = false; 
    _scheduleTimer.Interval = 120000; // 2 minutes in milliseconds 
    _scheduleTimer.Elapsed += delegate { _scheduleEvent.Set(); } 

    // Create the thread using anonymous method. 
    _thread = new Thread(delegate() { 
     // Create the WaitHandle array. 
     WaitHandler[] handles = new WaitHandle[] { 
      _shutdownEvent, 
      _scheduleEvent 
     }; 
     // Start the timer. 
     _scheduleTimer.Start(); 
     // Wait for one of the events to occur. 
     while (!_shutdownEvent.WaitOne(0)) { 
      switch (WaitHandle.WaitAny(handles)) { 
       case 0: // Shutdown Event 
        break; 
       case 1: // Schedule Event 
        _scheduleTimer.Stop(); 
        _scheduleEvent.Reset(); 
        ThreadPool.QueueUserWorkItem(PerformScheduledWork, null); 
        break; 
       default: 
        _shutdownEvent.Set(); // should never occur 
      } 
     } 
    }); 
    _thread.IsBackground = true; 
    _thread.Start(); 
} 
protected override void OnStop() 
{ 
    // Signal the thread to shutdown. 
    _shutdownEvent.Set(); 
    // Give the thread 3 seconds to terminate. 
    if (!_thread.Join(3000)) { 
     _thread.Abort(); // not perferred, but the service is closing anyway 
    } 
} 
private void PerformScheduledWork(object state) 
{ 
    // Perform your work here, but be mindful of the _shutdownEvent in case 
    // the service is shutting down. 
    // 
    // Reschedule the work to be performed. 
    _scheduleTimer.Start(); 
} 
+0

Esta es una buena respuesta y en mi humilde opinión una solución mejor que la respuesta seleccionada –

2

Alguien tenía una pregunta similar sobre Superusuario. Puede instalar una herramienta que supervisa los servicios de Windows. Algo como Service Hawk lo ayudaría a mantener los servicios iniciados, o le permitirá programar reinicios automáticos (posiblemente durante la noche) para mantener el servicio funcionando sin problemas.

0

Apliqué una combinación del temporizador y el hilo de trabajo, y hasta ahora funciona bien.

Configuré mi temporizador para marcar cada minuto y, si el tiempo coincide con el horario, creo mi hilo de trabajo.

que utiliza esta técnica en hacer un poco de limpieza:

if (nowTimeVar.ToShortTimeString().CompareTo(houseKeep.ToShortTimeString()) == 0) 
{ 
    myHouseKeeper = new clsHouseKeep(archiveFolder, lastday, myCounter, myLogger);     
}  
0

después de verificar la cantidad de tutorial desarrollado el servicio de ventanas que se ejecutan periódicamente cada 60 minutos y se iniciará el proceso en segundo plano y ejecutar Prueba.exe.

public partial class Scheduler : ServiceBase 
{ 
    private Timer timer1 = null; 


    public Scheduler() 
    { 
     InitializeComponent(); 
    } 
    // Get the Exe path 
    private string GetPath() 
    { 
     Assembly assembly = Assembly.GetExecutingAssembly(); 
     return Path.Combine(Path.GetDirectoryName(assembly.Location), "Test.exe"); 

    } 
    // you can right your own custom code 
    // Above vista version the process works as Administrator- "runas" and in old OS it will Start process as per Administartor rights 
    public void startprocess() 
    { 
     // System.Diagnostics.Process.Start(GetPath()); 
     System.Diagnostics.Process process = null; 
     System.Diagnostics.ProcessStartInfo processStartInfo; 

     processStartInfo = new System.Diagnostics.ProcessStartInfo(); 

     processStartInfo.FileName = GetPath(); 

     if (System.Environment.OSVersion.Version.Major >= 6) // Windows Vista or higher 
     { 
      processStartInfo.Verb = "runas"; 
     } 
     else 
     { 
      // No need to prompt to run as admin 
     } 

     processStartInfo.Arguments = ""; 

     process = System.Diagnostics.Process.Start(processStartInfo); 

    } 
    // On start method will load when system Start 
    // timer1_tick Event will call every hour after once start the service automatically 
    protected override void OnStart(string[] args) 
    { 
     //System.Diagnostics.Debugger.Launch(); 
     timer1 = new Timer(); 
     this.timer1.Interval = 3600000; //every 60 min 
     this.timer1.Elapsed += new System.Timers.ElapsedEventHandler(this.timer1_Tick); 
     timer1.Enabled = true; 

    } 

    // Timer1_tick will Start process every hour 
    private void timer1_Tick(object sender, ElapsedEventArgs e) 
    { 
     //Write code here to do some job depends on your requirement 
     startprocess(); 
    } 
    // Method will stop the service 
    // we have set the method stop service as false. so service will not stop and run every hour 
    protected override void OnStop() 
    { 
     timer1.Enabled = false; 

    } 

}

Cuestiones relacionadas