Thread Sleep и службы Windows - PullRequest
7 голосов
/ 09 марта 2010

Я работаю над службой Windows, которая имеет некоторые проблемы с Thread.Sleep(), поэтому я решил, что вместо этого попробую использовать таймер, так как этот вопрос рекомендует:

Использование Thread.Sleep () в службе Windows

Дело в том, что мне не совсем понятно, как можно это реализовать. Я верю, что так, но я просто хотел убедиться:

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

Помимо использования VB.Net (шучу, но я бы использовал C #, если бы мог.) Я здесь на правильном пути? Это лучше, чем использовать Thread.Sleep()?

Ответы [ 2 ]

19 голосов
/ 09 марта 2010

Честно говоря, я должен был бы сказать, что я думаю, что вы немного сбились с пути.

Прежде всего, фактический код не имеет смысла; рабочий поток ожидает сигнала, но без причины - на самом деле он не находится в каком-либо цикле или ожидает ресурса. Во-вторых, если вам нужно было выполнить какой-то (возможно, пропущенный) код очистки на рабочем месте после получения сигнала завершения работы, ваша служба может не дать рабочему потоку достаточно времени для очистки.

Но, что более важно, все, что вы сделали, это перенесли проблему в отдельный поток. Это может держать службу отзывчивой к контроллеру службы, но это не решает проблему проектирования - и это добавляет много ненужной сложности с использованием потоков и мьютексов; в конечном счете, это только усложнит отладку вашего сервиса, если вам когда-либо понадобится.

Допустим, вам нужно выполнять некоторый код каждые 5 секунд. Идея состоит в том, чтобы вместо «ожидания» 5 секунд вы инвертировали элемент управления и позволяли таймеру вызывать вашу работу.

Это плохо:

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

Это хорошо:

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

Вот и все. Это все, что вам нужно сделать, чтобы делать работу через равные промежутки времени. Пока сама работа не выполняется в течение нескольких секунд, ваша служба будет реагировать на контроллер службы. Вы можете даже поддержать паузу в этом сценарии:

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

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

Единственный раз, когда вам нужно начать создавать рабочие потоки в вашем сервисе, это когда сама работа может выполняться очень долго, и вам нужна возможность отмены в середине. Это, как правило, требует значительных усилий по проектированию, поэтому я не буду вдаваться в это, не зная наверняка, что это связано с вашим сценарием.

Лучше не начинать внедрять многопотоковую семантику в ваш сервис, если вам это действительно не нужно. Если вы просто пытаетесь запланировать выполнение небольших задач, вам это точно не нужно.

1 голос
/ 09 марта 2010

Я недавно написал сервис, который должен был выполнить некоторую работу, затем подождать n минут, а затем выполнить цикл.

Я также использовал ManualResetEvent (называемый _evt) и вызов Set () в OnStop. Но мне не нужен был таймер.

В служебной ветке у меня было:

for (;;) {

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

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

Итак, истекло время ожидания => время для дальнейшей работы. Или вызывается Set () и цикл прерывается.

...