2011-12-01 12 views
6

Ho un processo che viene eseguito nella propria thread e può essere avviato/arrestato senza bloccare. Questo alla fine andrà in un servizio di Windows, ma lo sto configurando in un'app console per ora fino a quando non è completamente aggiornato.Utilizzo di un semaforo anziché di un ciclo while. Questo è buono o cattivo?

Dopo la chiamata a Start(), voglio bloccare il thread del programma principale finché non viene premuto Ctrl-C. So che questo funzionerà:

public static void Main(string[] args) 
{ 
    bool keepGoing = true; 

    var service = new Service(); 

    System.Console.TreatControlCAsInput = false; 

    System.Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) 
    { 
     e.Cancel = true; 
     service.Stop(); 
     keepGoing = false; // Break the while loop below 
    }; 

    service.Start(); 

    while(keepGoing) 
    { 
     Thread.Sleep(100); // 100 is arbitrary 
    } 

} 

Tuttavia, ritengo che la bandiera e il valore del sonno arbitrario fastidio. So che il costo della CPU è praticamente 0 nel ciclo while, ma preferirei avere un blocco "hard" che viene rilasciato non appena viene eseguito il gestore Ctrl-C. Ho ideato il sotto, utilizzando un semaforo per bloccare fino a quando il gestore anonimo Ctrl-C è fatto:

public static void Main(string[] args) 
{ 
    var service = new Service(); 

    var s = new Semaphore(1, 1); 

    System.Console.TreatControlCAsInput = false; 

    System.Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) 
    { 
     e.Cancel = true; 
     service.Stop(); 
     s.Release(); // This will allow the program to conclude below 
    }; 

    service.Start(); 

    s.WaitOne(); // This will not block 
    s.WaitOne(); // This will block w/o CPU usage until the sempahore is released 

} 

E 'un brutto disegno? È eccessivo? È pericoloso?

EDIT:

Ho anche collego AppDomain.CurrentDomain.UnhandledException come segue:

AppDomain.CurrentDomain.UnhandledException += delegate { 
    service.Stop(); 
    s.Release(); 
}; 

EDIT 2 °:

Vorrei sottolineare che è fondamentale che il Il metodo Stop() viene richiamato all'uscita. @Adam Ralph ha un modello perfettamente valido per una console ibrida/servizio, ma non aveva queste informazioni quando rispondeva alla Q.

+1

Se riesci a evitare il ciclo while, dico che vale la pena di proseguire. – ChaosPandion

+1

Per la prototipazione, quest'ultima rappresenta un notevole miglioramento. In un'applicazione di produzione eviterei l'intera idea di pausa "CTRL + C". Usa un segnale per uccidere il thread. Il modo in cui il segnale è 'Set()' dipende dagli altri livelli coinvolti. Tieni questo a mente quando progetti il ​​tuo servizio. Aggiungi un mezzo, come un metodo che può essere chiamato a, a sua volta, chiama 'Set()'. –

+0

@ P.Brian.Mackey: Sebbene questa non sia un'applicazione di produzione, se * fosse *, non sarebbe prudente gestire almeno Ctrl-C con grazia in un'app per console non interattiva? Così com'è, Ctrl-C semplicemente spegnerà il programma senza l'opportunità di ripulire prima di uscire. In definitiva, mi stavo semplicemente chiedendo se la soluzione semaforo per l'opzione "while (flag) Thread.Sleep (...)". –

risposta

4

Abbiamo un requisito simile in alcune delle nostre app. Sono servizi di Windows, ma per il debug spesso vogliamo eseguirli come app per console. Inoltre, di solito codifichiamo nuove app come servizi di Windows abbastanza presto, ma spesso non vogliamo doverle eseguire come servizio fino a tardi, una volta dimostrato il concetto, ecc.

Questo è lo schema che abbiamo uso: -

using (var service = new Service()) 
{ 
    if (Environment.UserInterActive) 
    { 
     service.Start(); 
     Thread.Sleep(Timeout.Infinite); 
    } 
    else 
    { 
     ServiceBase.Run(service); 
    } 
} 

Raccontare il filo a dormire infinitamente potrebbe sembrare inefficiente, ma questo è solo per gli scenari di debug e il filo ridondante non costa tempo di CPU, solo alcuni di memoria (circa 1 MB), che è principalmente composto dal impilare lo spazio allocato alla discussione. Il processo può ancora essere chiuso con Ctrl + C o chiudendo la finestra di comando.

- EDIT -

Se si scopre che service.Dispose() non viene chiamato quando si preme CTRL + C (vale a dire un'interruzione maleducata accade) e la chiamata alla Dispose() è fondamentale, quindi penso che si potrebbe fare in modo esplicito questo modo: -

using (var service = new Service()) 
{ 
    if (Environment.UserInterActive) 
    { 
     Console.CancelKeyPress += (sender, e) => service.Dispose(); 
     service.Start(); 
     Thread.Sleep(Timeout.Infinite); 
    } 
    else 
    { 
     ServiceBase.Run(service); 
    } 
} 

Nota che Stop() devono essere passate tra Dispose().

+0

Questo problema non si verifica ancora dove, in un contesto 'UserInterActive', se l'utente dovesse tentare di interrompere il processo (usando Ctrl-C o finestra chiusa), il metodo' Stop() 'non verrebbe chiamato ? Questo è quello che sto cercando di realizzare nel mio esempio sopra. –

+0

Nei nostri servizi, chiamare il metodo Stop non è cruciale, ma suppongo che si possa ancora iscriversi a CancelKeyPress e farlo esplicitamente. Modificherò la risposta –

+0

Non sono stato chiaro su questo nella Q; perdonami per quello. Ho modificato menzionando questo. –

Problemi correlati