2012-05-17 10 views
5

Tengo un servicio de ventanas multiproceso en .Net 3.5 y estoy teniendo problemas para detener el servicio correctamente cuando se crea más de un subproceso.Detener un servicio de ventanas con subprocesos múltiples

Este servicio solía crear solo un hilo para hacer todo el trabajo, y acabo de cambiarlo para que sea multihilo. Funciona perfectamente, pero cuando se detiene el servicio, si se está ejecutando más de un subproceso, se bloqueará el servicio hasta que se completen todos los subprocesos.

Cuando se inicia el servicio, puedo crear un subproceso de fondo para manejar el proceso principal:

protected override void OnStart(string[] args) 
    { 
     try 
     { 
      //Global variable that is checked by threads to learn if service was stopped 
      DeliveryConstant.StopService = false; 
      bool SetMaxThreadsResult = ThreadPool.SetMaxThreads(10, 10); 

      ThreadStart st = new ThreadStart(StartThreadPool); 
      workerThread = new Thread(st); 
      workerThread.IsBackground = true; 
      serviceStarted = true; 
      workerThread.Start(); 
     } 
     catch (Exception ex) 
     { 
      //Log something; 
     } 

Aquí es el método StartThreadPool:

//Tried with and without this attribute with no success... 
    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)] 
    public void StartThreadPool() 
    { 
     while (serviceStarted) 
     { 
      ProcessInfo input = new ProcessInfo(); 

      try 
      { 
       int? NumPendingRequests = GetItems(50, (Guid?)input.ProcessID); 

       if (NumPendingRequests > 0) 
       { 
        input.ProcessType = 1; 
        input.ProcessID = Guid.NewGuid(); 
        ThreadPool.QueueUserWorkItem(new WaitCallback(new DispatchManager().ProcessRequestList), input); 
       } 
      } 
      catch (Exception ex) 
      { 
       //Some Logging here 
      } 
     } 

     DeliveryConstant.StopService = true; 
    } 

he creado una variable estática en una clase separada para notificar a los hilos que el servicio fue detenido. Cuando el valor de esta variable es cierto, todas las discusiones deben detener el bucle principal (una para cada bucle):

 public static bool StopService; 

Finalmente, el método OnStop:

protected override void OnStop() 
    { 
     DeliveryConstant.StopService = true; 

     //flag to tell the worker process to stop 
     serviceStarted = false; 

     workerThread.Join(TimeSpan.FromSeconds(30)); 
    } 

En el método ProcessRequestList, en el Al final de cada foreach, compruebo el valor de la variable StopService. Si es cierto, rompo el ciclo.

Aquí está el problema: Los hilos se crean en trozos de 50 elementos. Cuando tengo 50 elementos o menos en la base de datos, solo se crea un hilo, y todo funciona maravillosamente. Cuando tengo más de 50 elementos, se crearán varios subprocesos, y cuando intento detener el servicio, no se detiene hasta que se completan todos los subprocesos de fondo.

De los registros, puedo ver que el método OnStop solo se ejecuta DESPUÉS de que se completen todos los hilos.

¿Alguna pista de lo que se podría cambiar para solucionarlo?

+0

Intenta poner Sleep (50) en el ciclo while. –

+0

Me parece que usted no ha incluido la parte más importante del código. Esa es la implementación del método ProcessRequestList. –

+0

¿Se puede mostrar el código donde se interroga a StopService? Busque utilizar CancellationToken sobre var estática, ya que está hecho para ese tipo de cosas. – hatchet

Respuesta

9

Este blog answer indica que no se llama a OnStop hasta que se completen todas las tareas de ThreadPool, lo cual es nuevo para mí, pero podría explicar su problema.

He desplegado muchos Servicios de Windows de subprocesos múltiples, pero prefiero crear mis propios hilos de fondo en lugar de usar el ThreadPool, ya que estos son hilos de larga ejecución. Instalo clases de trabajadores y lanzo su método DoWork() en el hilo. También prefiero usar devoluciones de llamada a la clase de lanzamiento para verificar la señal de parada y el estado del pase en lugar de solo hacer una prueba en contra de una variable global.

+0

Has encontrado exactamente el problema. ¡Muchas gracias! Esto es más o menos lo que dije que estaba pasando, pero no sabía que era un comportamiento esperado. Sabiendo eso, ahora estoy verificando el estado del servicio en los hilos de trabajo, y funciona como un amuleto. Sin embargo, aún no estoy seguro (todavía) de lo costoso que será el rendimiento. ¡Gracias! – Roberto

+0

Roberto, ¿te importaría publicar tu solución en tu pregunta, o es esa la variable booleana StopService? – PAULDAWG

3

Se pierden las barreras de memoria en los accesos a StopService, lo que puede ser un problema si tiene varias CPU. Bloquee mejor cualquier objeto de referencia para TODOS los accesos a la variable compartida. Por ejemplo:

object @lock; 

... 

lock (@lock) 
{ 
    StopService = true; 
} 

Editar: Como otra respuesta ha revelado, esta cuestión no era un problema de bloqueo, pero estoy dejando esta respuesta aquí como una cosa a comprobar con esquemas de sincronización multihilo.

Convertir la variable compartida en volátil también funcionaría en muchos casos, pero es más complejo probar la corrección porque no emite vallas completas.

+0

No se trata de atomicidad sino de barreras de memoria. La atomicidad significa que el otro hilo lo verá todo, o ninguno. La barrera de memoria (hay dos tipos de ella en juego aquí, pero saltémonos por simplicidad) asegura que el caso "ninguno" no ocurra. –

+0

Está etiquetado C#, no java – hatchet

+0

Vaya. Estaba saltando entre las preguntas. Devolvió esa mala interpretación aquí. –

Cuestiones relacionadas