2015-10-08 8 views
0

Обычно вы посоветовали использовать что-то подобное (с использованием тайм-аутов):Обычная практика для опроса внутри Windows, услуги

Thread workerThread = null; 
AutoResetEvent finishedEvent = new AutoResetEvent(false); 

protected override void OnStart(string[] args) { 
    this.finishedEvent.Reset(); 
    this.workerThread = new Thread(this.Poll); 
    this.workerThread.Start(); 
} 

protected override void OnStop() { 
    this.finishedEvent.Set(); 
    if(!this.workerThread.Join(2000)) { 
     this.RequestAdditionalTime(5000); 
    } 
} 

где Poll функция определяется как:

private void Poll() { 
    try { 
     var timeout = Int32.Parse(ConfigurationManager.AppSettings["pollingTimeout"]); 
     while(!this.finishedEvent.WaitOne(timeout, false)) { 
      // do polling 
     } 
    } 
    catch(Exception ex) { 
     Logger.Log.Fatal(ex); 
     throw; 
    } 
} 
  1. Являются ли эти конструкции по существу равными:

    while(!this.finishedEvent.WaitOne(0, false))

    и

    while(true), без finishedEvent?

  2. Я читал, что таймауты используются для уменьшения использования процессора. Это плохой выбор для использования опроса без тайм-аутов?
+1

Нет, они не эквивалентны. Вы никогда не сможете остановить службу с циклом while (true). И вы сожжете 100% ядро, ничего не получив. Опрос плох, но иногда у вас просто нет выбора, потому что вы не можете получить событие, чтобы сказать вам, что произошло что-то интересное. Зависит. –

+0

Хорошо, я понимаю! Таким образом, использование 'AutoResetEvent', когда вы находитесь в отдельном (рабочем) потоке, - это способ оповестить службу на ** stop **/** start **. – lexeme

ответ

1

Существует очень простой способ сделать это, если вам не требуется строгое выключение. Если вы пометите workerThread как background thread, он автоматически отключится, когда служба остановится. В этом примере вы можете отказаться от использования finishedEvent и использовать бесконечный цикл. Например,

Thread workerThread = null; 

protected override void OnStart(string[] args) 
{ 
    this.workerThread = new Thread(this.DoWork); 
    // Signal the thread to stop automatically when the process exits. 
    this.workerThread.IsBackground = true; 
    this.workerThread.Start(); 
} 

protected override void OnStop() 
{ 
} 

private void DoWork() 
{ 
    try 
    { 
     while (true) 
     { 
      // do your work here... 
     } 
    } 
    catch (Exception ex) 
    { 
     // handle exception here... 
    } 
} 

Обратите внимание, что этот подход следует использовать только в случае, если работа, которую вы делаете, может быть прервана в любой момент без побочных эффектов. Предположим, например, что вы пишете данные в электронную таблицу Excel. После выхода службы Windows поток, представленный вашим методом DoWork(), также немедленно выйдет. Если он находится в середине добавления данных в электронную таблицу, очень вероятно, что электронная таблица может иметь неполную информацию или, что еще хуже, даже может быть в состоянии, которое она не может быть открыта в Excel. Дело в том, что такой подход можно использовать, но только при определенных обстоятельствах.

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

Thread _workThread = null; 
// I use ManualResetEvent instead of AutoResetEvent because I do NOT want 
// this event to EVER reset. It is meant to be set exactly one time. 
ManualResetEvent _shutdownEvent = new ManualResetEvent(false); 

protected override void OnStart(string[] args) 
{ 
    _workThread = new Thread(DoWork()); 
    _workThread.Start(); 
} 

protected override void OnStop() 
{ 
    // Trigger the DoWork() method, i.e., the _workThread, to exit. 
    _shutdownEvent.Set(); 

    // I always shutdown my service by simply joining the work thread. 
    // There are probably more advanced techniques that take into account 
    // longer shutdown cycles, but I design my worker thread(s) to have 
    // tight work cycles so that the shutdownEvent is examined frequently 
    // enough to facilitate timely shutdowns. 
    _workThread.Join(); 
} 

Теперь давайте посмотрим на детали метода DoWork(). В этом примере я собираюсь использовать таймер, чтобы проиллюстрировать подход, основанный на событиях. Обратите внимание, что эта иллюстрация существенно не отличается от вызова метода WaitOne() с таймаутом. Однако, если выполняемая работа включает обработку ввода от других потоков, например потока, который принимает данные из сетевого сокета или потока, который считывает данные из базы данных, этот подход легко адаптирует эти сценарии.

// Creature of habit. AutoResetEvent would probably work for this event, 
// but I prefer to manually control when the event resets. 
ManualResetEvent _timerElapsedEvent = new ManualResetEvent(false); 
System.Timers.Timer _timer = null; 

private void DoWork() { 
    try { 
     // Create, configure, and start the timer to elapse every second and 
     // require a manual restart (again, I prefer the manual control). 
     // Note when the timer elapses, it sets the _timerElapsedEvent. 
     _timer = new Timer(1000) { AutoReset = false }; 
     _timer.Elapsed =+ (sender, e) => _timerElapsedEvent.Set(); 
     _timer.Start(); 

     // Create a WaitHandle array that contains the _shutdownEvent and 
     // the _timerElapsedEvent...in that order! 
     WaitHandle[] handles = new WaitHandle[] { _shutdownEvent, _timerElapsedEvent }; 

     // Employ the event-based mechanism. 
     while (!_shutdownEvent.WaitOne(0)) { 
      switch (WaitHandle.WaitAny(handles) { 
       case 0: 
        // This case handles when the _shutdownEvent occurs, 
        // which will cause the while loop to exit. 
        break; 
       case 1: 
        // This case handles when the _timerElapsedEvent occurs. 
        // Do the work, reset the event, and restart the timer. 
        DoProcessing(); 
        _timerElapsedEvent.Reset(); 
        _timer.Start(); 
        break; 
      } 
     } 
    } catch (Exception ex) { 
     // handle exception here... 
    } 
} 

WaitHandle массив, что делает механизм на основе событий возможно. Когда вы создаете массив, всегда не забудьте добавить события в массив в порядке приоритета. Вот почему _shutdownEvent указан до_timerElapsedEvent. Если события были отменены в массиве, возможно, что _shutdownEvent никогда не будет обработан. При необходимости вы можете добавить столько событий в массив WaitHandle. Это делает этот подход настолько гибким.

Последнее рассматривается.Чтобы облегчить своевременное завершение работы вашего сервиса, вы хотите убедиться, что работа, которая будет выполняться при запуске _timerElapsedEvent, не займет слишком много времени. Другими словами, _shutdownEvent не будет проверяться контуром while до выхода из системы DoProcessing(). Таким образом, вы захотите ограничить количество времени, которое вы проводите внутри метода DoProcessing(). Если этот метод длительный, то вы, вероятно, захотите проверить _shutdownEvent внутри DoProcessing() и выйти в стратегических точках, когда служба указала, что он выключен.

Надеюсь, это поможет.

+1

Отличное объяснение! Спасибо за ваш ответ. Да благословит вас Бог во имя Иисуса! – lexeme

+1

Вы тоже, брат. ;-) –