2013-08-25 7 views
5

При запуске через диспетчер служб управления службам Windows необходимо предположить, что методы обработки команд (OnStart, OnStop и т. Д.) Могут быть вызваны в разных потоках ни с чем, обеспечивая, чтобы, например, назначения членов были видны между методами?Нужны ли службам Windows, чтобы команды могли обрабатываться на разных потоках?

public class MyService : ServiceBase { 

    private Object _foo;  

    protected override void OnStart(string[] args) { 
     _foo = new Object(); 
    } 

    protected override void OnStop() { 
     if (_foo == null) { 
      throw new Exception("Assignment not visible"); // Can this happen? 
     } 

     _foo = null; 
    } 

} 

Я не могу найти гарантию того, что исключение в моем примере не будет выброшено, но все примеры, которые я нашел, в том числе elsewhere on StackOverflow, кажется, предположить, что, например, задания переменных в OnStart() всегда будет отображаться в OnStop().

Если SCM не гарантирует такую ​​гарантию, я знаю, как обеспечить, чтобы назначение было видимым (например, путем добавления блокировки для всех операций чтения/записи в службе). Меня интересуют, нужны ли такие меры.

+1

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

+2

Я задал тот же вопрос раньше. Я выкопаю его. – spender

+1

Здесь вы (обозначение как обман): [Calling ServiceBase.OnStart и OnStop ... тот же экземпляр?] (Http: // stackoverflow.com/questions/10799937/call-servicebase-onstart-and-onstop-same-instance) – spender

ответ

0

примера вы дали может в его же произошли только очень очень неправдоподобные например:

_foo = new VeryLongAndTimeConsummingTask(); 

[EDIT]: как упомянуто в комментариях SCM предотвращает OnStop от Запуска до завершения OnStart. этот комментарий исходит из моей возможной плохой практики разоблачения старта и остановки обертки как публичные.

Если событие остановки вызывается перед новым завершением, может случиться, что _foo имеет значение NULL;

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

+0

"если событие остановки вызывается до того, как' new' завершает его может случиться, что '_foo' равно null;" Не соответствует действительности в моем ответе. «а также' _foo' может быть выпущен в [другом] месте в коде, поэтому [это] хорошая практика, чтобы сначала проверить ». Правда. – J0e3gan

+0

@ J0e3gan nice spot Я предположил, что OnStart OnStop можно было вызывать из другого места, но да SCM dos не позволяет вам делать это случайным образом. –

2

В некотором смысле, SCM не может гарантировать, что исключение, которое вы начертите, не будет выбрано. Он не контролирует манипуляции с частным пользователем, например, если дополнительный служебный код влияет на _foo.

Сказав это, не рассмотрим следующий сценарий, чтобы увидеть, почему ответ на ваш конкретный вопрос явно не:

1) Создайте свою службу со следующими изменениями, чтобы продемонстрировать:

public partial class MyService : ServiceBase 
    { 
     private Object _foo; 
     private const string _logName = "MyService Log.txt"; // to support added logging 

     public MyService() 
     { 
      InitializeComponent(); 
     } 

     protected override void OnStart(string[] args) 
     { 
      // demonstrative logging 
      var threadId = Thread.CurrentThread.ManagedThreadId; 
      using (var log = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + _logName, true)) 
      { 
       log.WriteLine("{0}: In OnStart(string[]) on thread ID {1}. Sleeping for 10 seconds...", DateTime.Now, threadId); 
      } 

      // Sleep before initializing _foo to allow calling OnStop before OnStart completes unless the SCM synchronizes calls to the methods. 
      Thread.Sleep(10000); 

      _foo = new Object(); 
     } 

     protected override void OnStop() 
     { 
      // demonstrative logging added 
      var threadId = Thread.CurrentThread.ManagedThreadId; 
      using (var log = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + _logName, true)) 
      { 
       log.WriteLine("{0}: In OnStop() on thread ID {1}.", DateTime.Now, threadId); 
      } 

      if (_foo == null) 
      { 
       // demonstrative logging added 
       using (var log = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + _logName, true)) 
       { 
        log.WriteLine("{0}: _foo == null", DateTime.Now); 
       } 

       throw new Exception("Assignment not visible"); // Can this happen? 
      } 

      _foo = null; 
     } 
    } 

2) Откройте командную оболочку.

3) Откройте еще одну командную оболочку.

4) В первой командной оболочке установите службу (с sc create), если вы еще этого не сделали, и запустите ее (с помощью net start). Вы должны увидеть:

Служба MyService начинается .....

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

5) Во второй командной оболочке попытайтесь остановить службу (с net stop) до истечения 10 секунд. Вы должны увидеть:

Услуга запускается или останавливается. Пожалуйста, повторите попытку позже.

Таким образом, запуск службы - это операция блокировки, которая должна быть завершена до остановки службы.

6) Проверьте первую командную оболочку через 10 секунд. Вы должны увидеть:

Служба MyService была успешно запущена.

7) Вернитесь ко второй командной оболочке и попытайтесь снова остановить службу. Вы должны увидеть:

Служба MyService останавливается.

Служба MyService была успешно остановлена.

8) Обзор результирующий журнал - например:

10/22/2013 7:28:55 AM: В OnStart (строка []) на нити ID 4. Сон в течение 10 секунд ...

10/22/2013 7:29:17 AM: В OnStop() на нити ID 5.

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

Наконец, вы можете найти Mitchell Taylor (CoolDadTx) 's ответ a similar question in MSDN forums интересно, как я сделал:

потоковая модель используется SCM формально не задокументированы AFAIK. Известно, что каждая услуга вызывается в своем потоке. Однако SCM может использовать или не использовать пул потоков для повторного использования потока через службы. Когда вызывается услуга (запуск, остановка, пользовательские команды и т. Д.), Ожидается, что она выполнит свою задачу и вернется быстро. Существует ограничение на длительность этого срока. Что-то большее, чем быстрый возврат, требует, чтобы вы перенаправляли запрос на вторичный поток для обработки. Сам SCM работает на отдельном потоке, поэтому, если служба занимает слишком много времени, чтобы ответить, SCM видит, что он висел. Это обсуждается здесь: http://msdn.microsoft.com/en-us/library/ms684264(VS.85).aspx

UPDATE:

В частности, обратите внимание на статью MSDN Service State Transitions, к которой статья, в которой Mitchell Taylor приводит ссылки. Он содержит диаграмму состояния, которая довольно четко определяет & авторитетные документы, определяющие переходы состояния службы и выравнивание с тем, что я изложил выше. Он также объясняет в связи с диаграммой состояния, как SCM не передает запросы управления службами время от времени, чтобы обеспечить только определенные переходы состояния.