2012-05-17 15 views
5

Ho un servizio Windows multithread in .Net 3.5 e sto avendo qualche problema a interrompere correttamente il servizio quando viene creato più di un thread.Arresto di un servizio Windows multithreading

Questo servizio utilizzato per creare solo un thread per fare tutto il lavoro, e ho appena cambiato per essere multi-threaded. Funziona perfettamente, ma quando il servizio viene interrotto, se viene eseguito più di un thread, questo verrà sospeso fino al completamento di tutti i thread.

All'avvio del servizio, ho creare un thread in background per gestire il processo principale:

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

Ecco il metodo 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; 
    } 

ho creato una variabile statica in una classe separata per notificare ai thread che il servizio è stato interrotto. Quando il valore di questa variabile è vero, tutte le filettature devono arrestare il ciclo principale (una per ogni ciclo):

 public static bool StopService; 

Infine, il metodo OnStop:

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

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

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

Nel metodo ProcessRequestList, al Alla fine di ogni foreach, controllo il valore della variabile StopService. Se è vero, interrompo il ciclo.

Ecco il problema: I thread sono creati in blocchi di 50 elementi. Quando ho 50 elementi o meno nel database, viene creato solo un thread e tutto funziona perfettamente. Quando ho più di 50 articoli, verranno creati più thread e quando provo a interrompere il servizio, non si fermerà finché tutti i thread in background non saranno completati.

Dai log, posso vedere che il metodo OnStop viene eseguito solo DOPO che tutti i thread sono stati completati.

Qualsiasi indizio su cosa potrebbe essere modificato per risolvere il problema?

+0

Provare a mettere uno Sleep (50) nel ciclo while. –

+0

Mi sembra che tu non abbia incluso la parte più importante del codice. Questa è l'implementazione del metodo ProcessRequestList. –

+0

Puoi mostrare il codice in cui viene interrogato StopService? Cerca di usare CancellationToken su var statico, dato che è fatto per quel genere di cose. – hatchet

risposta

9

Questo blog answer indica che OnStop non viene chiamato fino a quando non vengono completate tutte le attività ThreadPool, che sono notizie per me ma spiegherebbero il problema.

Ho messo in campo molti servizi di Windows multi-thread ma preferisco creare i miei thread di background piuttosto che utilizzare il ThreadPool poiché questi sono thread con esecuzione prolungata. Istanzio le classi worker e lancio il loro metodo DoWork() sul thread. Inoltre, preferisco utilizzare le callback per la classe di avvio per verificare la presenza di un segnale di arresto e lo stato di passaggio piuttosto che semplicemente testare una variabile globale.

+0

Hai inchiodato precisamente il problema. Grazie molto! Questo è praticamente ciò che ho detto che stava succedendo, ma non sapevo che era un comportamento previsto. Sapendo che, ora sto controllando lo stato del servizio nei thread di lavoro, e funziona come un incantesimo. Non sono sicuro (ancora) quanto sia costoso per le prestazioni. Grazie! – Roberto

+0

Roberto, ti dispiacerebbe postare la tua soluzione nella tua domanda, o è quella che è la variabile booleana di StopService? – PAULDAWG

3

Mancano le barriere di memoria attorno agli accessi a StopService, che potrebbe essere un problema se si dispone di più CPU. Meglio bloccare qualsiasi oggetto di riferimento per TUTTI gli accessi alla variabile condivisa. Per esempio:

object @lock; 

... 

lock (@lock) 
{ 
    StopService = true; 
} 

Edit: Come un'altra risposta ha rivelato, questo problema non è stato un problema di bloccaggio, ma lascio questa risposta qui come una cosa da controllare con gli schemi di sincronizzazione multithread.

Rendere la variabile condivisa volatile potrebbe funzionare anche in molti casi, ma è più complesso dimostrarsi corretto perché non emette recinti completi.

+0

Non si tratta di atomicità ma di barriere della memoria. Atomicità significa che l'altro thread vedrà tutto questo o nessuno. Barriera della memoria (ce ne sono due in gioco qui, ma saltiamo quello per semplicità) assicura che il caso "nessuno" non avvenga. –

+0

È codificato C#, non java – hatchet

+0

Oops. Stavo saltando tra le domande. Rotolato indietro che misedit qui. –

Problemi correlati