Сон потока и Windows Services

Я работаю над службой Windows, с которой это имеет некоторые проблемы 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()?

7
задан Community 23 May 2017 в 10:27
поделиться

2 ответа

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

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

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

Допустим, вам нужно выполнять некоторый код каждые 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();
}

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

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

19
ответ дан 6 December 2019 в 09:19
поделиться

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

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

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

for (;;) {

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

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

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

1
ответ дан 6 December 2019 в 09:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: