2010-03-08 10 views
7

Estoy trabajando en un servicio de Windows que está teniendo algunos problemas con Thread.Sleep() así que pensé que iba a tratar de utilizar un temporizador en lugar ya que esta cuestión recomienda:sueño rosca y servicios de Windows

Using Thread.Sleep() in a Windows Service

cosa es que es No me queda del todo claro cómo se podría implementar esto. Creo que este es el camino, pero sólo quería asegurarse de que:

'' Inside The Service Object 
Dim closingGate As System.Threading.AutoResetEvent 

Protected Overrides Sub OnStart(ByVal args() As String) 
    Dim worker As New Threading.Thread(AddressOf Work) 
    worker.Start() 
End Sub 

Protected Sub Work() 
    Dim Program = New MyProgram() 
    closingGate = New System.Threading.AutoResetEvent(False) 
    Program.RunService(closingGate) 
End Sub 

Protected Overrides Sub OnStop() 
    closingGate.Set() 
End Sub 

'' Inside My Programs Code: 
Public Sub RunService(closingGate As AutoResetEvent) 
    Dim tmr As New Timer 
    '' ...and so on 

    closingGate.WaitOne() 
End Sub 

Aparte de usar VB.Net (es broma, pero estaría usando C#, si pudiera.) Estoy en el camino correcto aquí? ¿Es esto mejor que usar Thread.Sleep()?

+0

¿Por qué los nombres de las variables en mayúsculas? –

+0

Allí. Arreglado. –

Respuesta

19

Honestamente, tendría que decir que creo que estás un poco alejado de todo esto.

En primer lugar, el código publicado realmente no tiene sentido; el subproceso de trabajo está esperando una señal, pero sin ninguna razón; en realidad no está en un bucle de ningún tipo ni está esperando un recurso. En segundo lugar, si necesitó ejecutar algún código de limpieza (quizás omitido) en el trabajador después de recibir la señal de apagado, es posible que su servicio no le proporcione al trabajador suficiente tiempo para limpiar.

Pero, más fundamentalmente, todo lo que has hecho es mover el problema a un hilo separado. Esto puede mantener el servicio receptivo al controlador de servicio, pero no soluciona el problema de diseño, y agrega mucha complejidad innecesaria al usar los subprocesos y mutexes; en última instancia, solo hará que su servicio sea más difícil de depurar si alguna vez lo necesita.

Digamos que necesita ejecutar algún código cada 5 segundos. La idea es que, en lugar de "esperar" 5 segundos, inviertas el control y dejes que el temporizador invoque tu trabajo.

Esto es malo:

protected override void OnStart(string[] args) 
{ 
    while (true) 
    { 
     RunScheduledTasks(); // This is your "work" 
     Thread.Sleep(5000); 
    } 
} 

Esto es bueno:

public class MyService : ServiceBase 
{ 
    private Timer workTimer; // System.Threading.Timer 

    protected override void OnStart(string[] args) 
    { 
     workTimer = new Timer(new TimerCallback(DoWork), null, 5000, 5000); 
     base.OnStart(args); 
    } 

    protected override void OnStop() 
    { 
     workTimer.Dispose(); 
     base.OnStop(); 
    } 

    private void DoWork(object state) 
    { 
     RunScheduledTasks(); // Do some work 
    } 
} 

Eso es todo. Eso es todo lo que tienes que hacer para hacer el trabajo a intervalos regulares. Siempre que el trabajo en sí no se ejecute por segundos a la vez, su servicio seguirá siendo receptivo al controlador de servicio. Incluso puede apoyar haciendo una pausa en este escenario:

protected override void OnPause() 
{ 
    workTimer.Change(Timeout.Infinite, Timeout.Infinite); 
    base.OnPause(); 
} 

protected override void OnContinue() 
{ 
    workTimer.Change(0, 5000); 
    base.OnContinue(); 
} 

El único momento en que necesita para empezar a crear subprocesos de trabajo en su servicio es cuando el trabajo en sí puede funcionar durante un tiempo muy largo y que necesita la capacidad de cancelar en la mitad. Eso tiende a involucrar una gran cantidad de esfuerzo de diseño, por lo que no entraré en eso sin saber con certeza que está relacionado con su escenario.

Es mejor no empezar a introducir la semántica multiproceso en su servicio a menos que realmente lo necesita. Si solo está tratando de programar pequeñas unidades de trabajo, definitivamente no lo necesita.

+0

Por alguna razón, tuve la impresión de que si no tenía un hilo ejecutándose, el servicio se interrumpiría. –

+0

@Spencer Ruport: Todavía hay un hilo conductor cuando el 'método OnStart' termina, mismo que todavía hay un subproceso en ejecución cuando' OnLoad' termina en una WinForms 'form'. Después de ejecutar su código 'OnStart', el servicio entra en un bucle de mensajes donde espera señales del controlador de servicio. – Aaronaught

+0

¿Qué sucede si RunScheduledTasks() tarda en ejecutarse un tiempo impredecible y potencialmente largo? En el contexto del ejemplo anterior, digamos que puede tardar hasta 15 segundos en ejecutarse. En ese caso, el enfoque del temporizador ya no funcionaría, ¿verdad? Quiero decir que la idea sería tener 5 segundos entre cada llamada a RunScheduledTasks(). En ese caso, el enfoque Thread.Sleep parece más adecuado para el trabajo, a menos que me falta algo? –

1

Hace poco escribí un servicio que tenía que trabajar un poco, luego esperé n minutos y luego repetí.

También utilicé un ManualResetEvent (llamado _evt) y una llamada a Set() en OnStop. Pero no necesitaba un temporizador.

En el hilo servicio, que tenían:

for (;;) { 

    // do some work 
    if (_evt.WaitOne(_waitInterval)) { 

     // got the signal => time to leave 
     break; 
    } 
} 

Así, ya sea un tiempo de espera se produjo => tiempo para un poco más de trabajo. O se llama a Set() y el ciclo finaliza.

+0

Interesante. ¿Cuánto tiempo tomó el trabajo? ¿Habría momentos en que presionar Stop en Windows se colgaría mientras el código volvía a 'WaitOne (n)'? –

+0

El trabajo podría tomar un par de segundos. Lo primero que hago en OnStop es decirle al SCM que por favor espere 10 segundos. – Timores