2010-03-08 18 views
7

Sto lavorando a un servizio di Windows che sta avendo alcuni problemi con Thread.Sleep() così ho pensato che avrei cercato di utilizzare un timer invece come questa domanda raccomanda:sonno Discussione e servizi Windows

Using Thread.Sleep() in a Windows Service

cosa è che è non mi è del tutto chiaro come si possa implementare questo. Credo che questo sia il modo, ma ho voluto solo per assicurarsi che:

'' 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 

Oltre a utilizzare VB.Net (scherzando, ma sarei usando C#, se potessi.) Sono sulla strada giusta? È meglio di usare Thread.Sleep()?

+0

Perché i nomi delle variabili sono in maiuscolo? –

+0

Lì. Aggiustato. –

risposta

19

Onestamente, dovrei dire che penso che tu sia un po 'fuori mano qui.

Prima di tutto, il codice effettivo pubblicato non ha molto senso; il thread di lavoro è in attesa di un segnale, ma senza motivo - non è in realtà in un ciclo di qualsiasi tipo o in attesa di una risorsa. In secondo luogo, se hai avuto bisogno di eseguire qualche (forse omesso) codice di ripulitura nel lavoratore dopo aver ricevuto il segnale di spegnimento, il tuo servizio potrebbe non dare al thread di lavoro abbastanza tempo per ripulire.

Ma, più fondamentalmente, tutto ciò che hai fatto è spostare il problema su un thread separato. Ciò può mantenere il servizio reattivo al controller di servizio ma non risolve il problema di progettazione e aggiunge un sacco di complessità non necessaria utilizzando i thread e i mutex; in ultima analisi, renderà il tuo servizio più difficile eseguire il debug se ne avrai la necessità.

Supponiamo che sia necessario eseguire qualche codice ogni 5 secondi. L'idea è, invece di "aspettare" 5 secondi, di invertire il controllo e lasciare che il timer invochi il tuo lavoro.

Questo è un male:

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

Questo è un bene:

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

Questo è tutto. Questo è tutto quello che devi fare per lavorare a intervalli regolari. Finché il lavoro non viene eseguito per secondi alla volta, il servizio rimarrà sensibile al controller di servizio. Si può anche sostenere la pausa in questo scenario:

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

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

L'unico momento in cui è necessario per iniziare a creare i thread di lavoro nel servizio è quando l'opera stessa può funzionare per un tempo molto lungo ed è necessario la possibilità di cancellare in la metà. Questo tende a implicare una buona dose di impegno progettuale, quindi non entrerò in quello senza sapere con certezza che è legato al tuo scenario.

È meglio non iniziare a introdurre semantica multithread nel servizio a meno che non ne abbia davvero bisogno. Se stai solo provando a programmare piccole unità di lavoro da fare, sicuramente non ne hai bisogno.

+0

Per qualche motivo ho avuto l'impressione che se non avessi un thread in esecuzione il servizio sarebbe terminato. –

+0

@Spencer Ruport: C'è ancora un thread in esecuzione quando termina il metodo 'OnStart', come se ci fosse ancora un thread in esecuzione quando' OnLoad' finisce in un 'Form' di WinForms. Dopo aver eseguito il codice 'OnStart', il servizio entra in un loop di messaggi dove attende i segnali dal controller di servizio. – Aaronaught

+0

Cosa succede se RunScheduledTasks() richiede un tempo di esecuzione imprevedibile e potenzialmente lungo? Nel contesto dell'esempio precedente, diciamo che potrebbero essere necessari fino a 15 secondi per essere eseguiti. In quel caso l'approccio con il timer non funzionerebbe più, vero? Intendo dire che l'idea sarebbe di avere 5 secondi tra ogni chiamata a RunScheduledTasks(). In questo caso l'approccio Thread.Sleep sembra più adatto per il lavoro, a meno che mi manchi qualcosa? –

1

Recentemente ho scritto un servizio che doveva fare un po 'di lavoro, quindi attendere n minuti, quindi eseguire il ciclo.

Ho anche utilizzato un ManualResetEvent (chiamato _evt) e una chiamata a Set() in OnStop. Ma non avevo bisogno di un timer.

Nel thread di servizio, ho avuto:

for (;;) { 

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

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

Quindi, o si è verificato un timeout => tempo per un po 'più di lavoro. Oppure viene chiamato Set() e il ciclo termina.

+0

Interessante. Quanto tempo ci è voluto per il lavoro? Ci sarebbero momenti in cui il blocco di Stop in Windows si bloccava mentre il codice tornava a 'WaitOne (n)'? –

+0

Il lavoro potrebbe richiedere un paio di secondi. La prima cosa che faccio su OnStop è di dire all'SCM di aspettare per 10 secondi. – Timores