2010-03-08 17 views
7

Pracuję nad usługa systemu Windows, który jest posiadające pewne problemy z Thread.Sleep() więc pomyślałem, że chciałbym spróbować użyć czasomierza zamiast jak to pytanie poleca:uśpienia wątku i Windows Services

Using Thread.Sleep() in a Windows Service

Thing jest to nie do końca jasne, w jaki sposób można to zrealizować. Wierzę, że to jest sposób, ale chciałem się upewnić:

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

Poza zastosowaniem VB.Net (żartuję, ale będę przy użyciu C# gdybym mógł). Jestem na dobrej drodze tutaj? Czy to jest lepsze niż używanie Thread.Sleep()?

+0

Dlaczego nazwy zmiennych są pisane wielką literą? –

+0

Nie. Naprawione. –

Odpowiedz

19

Szczerze mówiąc, musiałbym powiedzieć, że myślę, że jesteś tu na uboczu.

Po pierwsze, rzeczywisty kod wysłany nie ma sensu; wątek roboczy oczekuje na sygnał, ale bez żadnego powodu - w rzeczywistości nie jest w pętli ani nie czeka na zasób. Po drugie, jeśli potrzebujesz wykonać (prawdopodobnie pominięty) kod czyszczenia w module roboczym po otrzymaniu sygnału zamknięcia, twoja usługa może nie dać pracownikowi wystarczającej ilości czasu na oczyszczenie.

Ale bardziej fundamentalnie, wszystko, co zrobiłeś, to przenieść problem do osobnego wątku. Dzięki temu usługa może reagować na kontroler usługi, ale nie rozwiązuje problemu z projektowaniem - i dodaje wiele niepotrzebnej złożoności, używając wątków i muteksów; w ostatecznym rozrachunku sprawi to, że twoja usługa będzie trudniejsza w debugowaniu, jeśli zajdzie taka potrzeba.

Załóżmy, że musisz wykonać kod co 5 sekund. Chodzi o to, że zamiast "czekać" 5 sekund, odwracasz kontrolę i pozwalasz timerowi wywoływać twoją pracę.

To jest złe:

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

To jest dobre: ​​

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

To wszystko. To wszystko, co musisz zrobić, aby wykonywać pracę w regularnych odstępach czasu. Dopóki praca nie będzie działać przez kilka sekund, twoja usługa będzie reagować na kontroler usługi. Można nawet wspierać zatrzymując w tym scenariuszu:

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

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

Jedyny czas, kiedy trzeba rozpocząć tworzenie wątków roboczych w usłudze jest, gdy sama praca może trwać bardzo długo i trzeba zdolność do odwołania w środek. To często wymaga dużego nakładu pracy nad projektem, więc nie będę się z tym wiązał, nie wiedząc na pewno, że jest to związane z twoim scenariuszem.

Najlepiej nie zaczynać wprowadzania semantyki wielowątkowej w swojej usłudze, chyba że naprawdę jej potrzebujesz. Jeśli chcesz tylko zaplanować małe, pracujące jednostki, to zdecydowanie nie potrzebujesz tego.

+0

Z jakiegoś powodu odniosłem wrażenie, że gdybym nie miał wątku, usługa przestałaby działać. –

+0

@Spencer Ruport: Wciąż jest wątek uruchomiony, gdy kończy się metoda 'OnStart', tak samo jak jest jeszcze wątek uruchomiony, gdy kończy się' OnLoad' w formularzu 'WinForms'. Po uruchomieniu kodu 'OnStart', usługa przechodzi do pętli komunikatów, w której oczekuje na sygnały od kontrolera usług. – Aaronaught

+0

Co zrobić, jeśli RunScheduledTasks() może trwać nieprzewidywalnie długo? W kontekście powyższego przykładu załóżmy, że uruchomienie może potrwać do 15 sekund. W takim przypadku metoda timera przestałaby działać, prawda? Chodzi mi o to, aby pomysł miał 5 sekund między każdym wywołaniem RunScheduledTasks(). W takim przypadku podejście Thread.Sleep wydaje się najlepiej pasować do pracy, chyba że czegoś brakuje? –

1

Niedawno napisałem usługę, która musiała wykonać trochę pracy, następnie odczekać n minut, a następnie zapętlić.

Użyłem również instrukcji ManualResetEvent (nazwanej _evt) oraz wywołania Set() w OnStop. Ale nie potrzebowałem timera.

w wątku usług, miałem:

for (;;) { 

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

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

Więc, albo nastąpiło przekroczenie limitu czasu => czas na trochę więcej pracy. Lub Set() zostaje wywołane, a pętla zostaje zakończona.

+0

Interesujące. Ile czasu zajęła praca? Czy byłyby chwile, kiedy naciśnięcie Stop w Windows zawiesza się, gdy kod wraca do 'WaitOne (n)'? –

+0

Praca może potrwać kilka sekund. Pierwszą rzeczą, jaką robię w OnStop, jest powiadomienie SCM, aby poczekał 10 sekund. – Timores