2013-08-25 3 views
5

Bei der Ausführung über den Dienststeuerungs-Manager müssen Windows-Dienste davon ausgehen, dass Befehlsverarbeitungsmethoden (OnStart, OnStop usw.) für verschiedene Threads aufgerufen werden können, ohne sicherzustellen, dass beispielsweise Zuordnungen zu Elementen zwischen Methoden sichtbar sind?Müssen Windows-Dienste sicherstellen, dass Befehle in verschiedenen Threads verarbeitet werden können?

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

} 

ich keine Garantie, dass die Ausnahme in meinem Beispiel werden nicht geworfen finden kann, aber alle die Beispiele, die ich gefunden habe, einschließlich elsewhere on StackOverflow scheint, dass, zum Beispiel Zuweisungen an Variablen zu übernehmen OnStart() wird immer in OnStop() sichtbar sein.

Wenn keine solche Garantie vom SCM geleistet wird, weiß ich, wie sichergestellt werden kann, dass die Zuweisung sichtbar ist (z. B. durch Hinzufügen einer Sperre um alle Lese-/Schreibvorgänge im Service). Mich interessiert, ob solche Maßnahmen notwendig sind.

+1

Ich bin daran interessiert, was die Garantien auch sind. Ich habe nie gesehen, dass etwas wie dein Beispiel ein Problem verursacht; Ich weiß jedoch, dass der Erwerb von Ressourcen, die an die Thread-Identität gebunden sind (in unserem Fall ein Mutex), in der Vergangenheit zu Problemen mit meinem Team geführt hat. –

+2

Ich habe die gleiche Frage schon einmal gestellt. Ich werde es ausgraben. – spender

+1

Hier sind Sie (Kennzeichnung als dupe): [Aufruf von ServiceBase.OnStart und OnStop ... gleiche Instanz?] (Http: // stackoverflow.com/questions/10799937/calling-servicebase-onstart-and-onstop-selbe-instanz) – spender

Antwort

0

das Beispiel, das Sie in gab kann hat seine nur sehr sehr unwahrscheinlich ex passieren:

_foo = new VeryLongAndTimeConsummingTask(); 

[EDIT]: wie in Kommentar SCM bezeichnet verhindert, dass die OnStop aus Laufen, bevor die OnStart abgeschlossen ist. Dieser Kommentar stammt aus meiner möglichen schlechten Praxis, die Start- und Stopp-Wrapper öffentlich zu machen.

Wenn das stop-Ereignis vor dem neuen Ende aufgerufen wird, kann es passieren, dass _foo null ist;

und auch _foo kann in Addiererplatz in Code freigegeben werden, so ist es eine gute Praxis, zuerst zu überprüfen.

+0

"wenn das stop-Ereignis aufgerufen wird, bevor das' new' es beendet kann passieren, dass '_foo' null ist;" Nicht wahr nach dem Beispiel in meiner Antwort. "und auch' _foo' können in [einem anderen] Ort im Code veröffentlicht werden, also ist es eine gute Übung, zuerst nachzusehen. " Wahr. – J0e3gan

+0

@ J0e3gan netter Ort Ich nahm an, der OnStart könnte ein OnStop von einem anderen Ort aus aufgerufen werden, aber ja, der SCM lässt es nicht zu, dass man es zufällig macht. –

2

In einer Hinsicht kann der SCM nicht garantieren, dass die von Ihnen umrissene Ausnahme nicht ausgelöst wird. Es steuert natürlich nicht die Manipulation eines privaten Mitglieds durch den Dienst - z. wenn sich zusätzlicher Servicecode auf _foo auswirkt.

Having said dies, das folgende Szenario betrachten, um zu sehen, warum die Antwort auf Ihre Frage eindeutig nicht ist:

1) Bauen Sie Ihren Service mit folgenden Änderungen zu demonstrieren:

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) Öffnen Sie eine Befehlsshell.

3) Öffnen Sie eine weitere Befehlsshell.

4) Installieren Sie in der ersten Befehlsshell den Dienst (mit sc create), falls noch nicht geschehen, und starten Sie ihn (mit net start). Sie sollten sehen:

Der MyService Dienst starten wird .....

der nachlauf Punkte nacheinander hinzugefügt werden soll, wie die SCM der 10 Sekunden wartet durch Schlaf den Dienst zu starten.

5) Versuchen Sie in der zweiten Befehlsshell den Dienst (mit net stop) zu stoppen, bevor 10 Sekunden verstreichen. Sie sollten folgendes sehen:

Der Dienst wird gestartet oder gestoppt. Bitte versuche es später erneut.

Das Starten eines Dienstes ist also eindeutig ein Blockiervorgang, der abgeschlossen werden muss, bevor der Dienst gestoppt werden kann.

6) Überprüfen Sie die erste Befehlsshell nach 10 Sekunden. Sie sollten Folgendes sehen:

Der MyService-Dienst wurde erfolgreich gestartet.

7) Kehren Sie zur zweiten Befehlsshell zurück und versuchen Sie, den Dienst erneut zu stoppen. Sie sollten Folgendes sehen:

Der MyService-Dienst wird angehalten.

Der MyService-Dienst wurde erfolgreich beendet.

8) Überprüfen des resultierenden log - zum Beispiel:

10/22/2013 7:28:55 AM: In OnStart (String []) auf Thread-ID 4 Schlafen für 10 Sekunden ...

10/22/2013 7:29:17 aM: In OnStop() auf Thread-ID 5.

Starten und den Dienst schnell zu stoppen ist einfacher mit zwei Befehlen-Shells ich denke; Das Beispiel funktioniert jedoch auch mit einer Befehlshülle.

Schließlich könnten Sie Mitchell Taylor (CoolDadTx) 's Antwort auf a similar question in MSDN forums interessant finden wie ich es tat:

Das Modell Einfädeln durch den SCM verwendet wird AFAIK nicht formal dokumentiert. Was bekannt ist, ist, dass jeder Dienst auf seinem eigenen Thread aufgerufen wird. Der SCM verwendet jedoch möglicherweise einen Thread-Pool, um einen Thread zwischen Diensten wiederzuverwenden. Wenn ein Dienst aufgerufen wird (Start, Stopp, benutzerdefinierte Befehle usw.), wird erwartet, dass er seine Aufgabe ausführt und schnell zurückkehrt. Es gibt eine starke Begrenzung, wie lange es dauern kann. Für mehr als eine schnelle Rückgabe müssen Sie die Anforderung zur Verarbeitung an einen sekundären Thread übergeben. Der SCM selbst läuft auf einem separaten Thread. Wenn ein Service zu lange braucht, um zu antworten, sieht der SCM ihn als unterbrochen. Dies wird hier diskutiert: http://msdn.microsoft.com/en-us/library/ms684264(VS.85).aspx

UPDATE:

Besonders die Service State Transitions MSDN beachten Sie Artikel, an dem der Artikel, die Mitchell Taylor zitiert Links. Es enthält ein Zustandsdiagramm, das ziemlich eindeutig & definierte Dienstzustandsübergänge autorisierend dokumentiert und mit dem übereinstimmt, was ich oben umrissen habe. In Bezug auf das Zustandsdiagramm wird auch erläutert, wie der SCM Dienststeuerungsanforderungen nicht zeitweise überträgt, um nur definierte Zustandsübergänge sicherzustellen.

Verwandte Themen