2011-12-01 9 views
6

मेरे पास एक प्रक्रिया है जो अपने स्वयं के धागे में चलती है और इसे अवरुद्ध किए बिना शुरू/बंद कर दिया जा सकता है। यह अंत में एक विंडोज सेवा में जाएगा, लेकिन मैं इसे अब तक कंसोल ऐप में स्थापित कर रहा हूं जब तक कि यह पूरी तरह से बाहर नहीं हो जाता है।लूप के बजाए एक सेमफोर का उपयोग करना। यह अच्छा है या बुरा है?

प्रारंभ करने के लिए कॉल के बाद(), मैं मुख्य प्रोग्राम थ्रेड को ब्लॉक-सी दबाए जाने तक अवरुद्ध करना चाहता हूं। मुझे पता है कि यह काम करेगा:

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 
    } 

} 

हालांकि, मुझे ध्वज और मनमानी नींद मूल्य परेशान लगता है। मुझे पता है कि सीपीयू लागत व्यावहारिक रूप से 0 लूप में है, लेकिन मेरे पास एक "हार्ड" ब्लॉक होगा जो जैसे ही Ctrl-C हैंडलर किया जाता है। मैं नीचे खोजा गया, जब तक गुमनाम Ctrl-C हैंडलर किया जाता है ब्लॉक करने के लिए एक सेमाफोर का उपयोग कर:

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 

} 

यह एक बुरा डिजाइन है? क्या यह अधिक है? यह खतरनाक है?

संपादित करें:

मैं भी AppDomain.CurrentDomain.UnhandledException ऊपर हुक इस प्रकार है:

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

संपादित करें 2:

मैं नोट करना चाहिए यह महत्वपूर्ण है कि यह है कि Stop() विधि बाहर निकलने पर बुलाया जाता है। @ एडम राल्फ के पास हाइब्रिड कंसोल/सेवा के लिए एक बिल्कुल अच्छा पैटर्न है, लेकिन Q.

+1

यदि आप थोड़ी देर से बच सकते हैं तो मैं कहता हूं कि यह पीछा करने लायक है। – ChaosPandion

+1

बाद वाले प्रोटोटाइप के लिए एक सुधार है। एक उत्पादन आवेदन में मैं पूरे "CTRL + C" ब्रेक विचार से बचूंगा। इसके बजाए धागे को मारने के लिए सिग्नल का प्रयोग करें। जिस तरह से सिग्नल 'सेट() 'शामिल है, अन्य परतों पर निर्भर करता है। अपनी सेवा को डिजाइन करते समय बस इसे ध्यान में रखें। एक साधन जोड़ें, एक विधि की तरह जिसे कॉल किया जा सकता है, बदले में, 'सेट() 'पर कॉल करें। –

+0

@ पी। ब्रायन।मैकी: हालांकि यह एक उत्पादन अनुप्रयोग नहीं है, अगर यह * था, तो क्या यह कम से कम एक गैर-इंटरैक्टिव कंसोल ऐप में Ctrl-C को गहन रूप से संभालने के लिए समझदार नहीं होगा? जैसा कि है, Ctrl-C बाहर निकलने से पहले साफ़ करने के अवसर के बिना प्रोग्राम को बंद कर देगा। आखिरकार, मैं बस सोच रहा था कि "जबकि (ध्वज) थ्रेड के सैमफोर समाधान। सो जाओ (...)" विकल्प। –

उत्तर

4

का जवाब देने पर यह जानकारी नहीं थी हमारे पास हमारे कुछ ऐप्स में एक समान आवश्यकता है। वे विंडोज सेवाएं हैं, लेकिन डिबगिंग के लिए हम अक्सर उन्हें कंसोल ऐप्स के रूप में चलाने के लिए चाहते हैं। इसके अलावा, हम आम तौर पर विंडोज़ सेवाओं के रूप में नए ऐप्स को कोड करते हैं, लेकिन अक्सर बाद में उन्हें एक सेवा के रूप में चलाने की ज़रूरत नहीं होती है, एक बार जब हम अवधारणा साबित कर लेते हैं, आदि

यह पैटर्न है उपयोग: -

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

असीम सोने के लिए धागा बोलने अक्षम लग सकता है, लेकिन यह केवल डीबगिंग परिदृश्यों के लिए है और अनावश्यक धागा कोई CPU समय बस कुछ स्मृति लागत, जो ज्यादातर से बना है (1MB के बारे में) धागे को आवंटित ढेर अंतरिक्ष। प्रक्रिया को अभी भी Ctrl + C से या कमांड विंडो बंद करके बाहर किया जा सकता है।

- संपादित करें -

आप पाते हैं कि service.Dispose() जब Ctrl + C दबाया जाता है कहा जाता है नहीं किया जा रहा है (यानी एक अशिष्ट बीच में बंद करें होता है) और Dispose() करने के लिए कॉल महत्वपूर्ण है, तो मुझे लगता है कि आप स्पष्ट रूप से कर सकता है इस तरह तो: -

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

ध्यान दें कि Stop()Dispose() में समाहित किया जाना चाहिए।

+0

क्या यह अभी भी समस्या से पीड़ित नहीं है, जहां 'UserInterActive' संदर्भ में, यदि उपयोगकर्ता प्रक्रिया को रोकने का प्रयास कर रहा था (Ctrl-C या विंडो बंद करें), तो' Stop() 'विधि को नहीं कहा जाएगा ? यही वह है जो मैं उपरोक्त मेरे उदाहरण में पूरा करने की कोशिश कर रहा हूं। –

+0

हमारी सेवाओं में, स्टॉप विधि को कॉल करना महत्वपूर्ण नहीं है, लेकिन मुझे लगता है कि आप अभी भी CancelKeyPress की सदस्यता ले सकते हैं और इसे स्पष्ट रूप से कर सकते हैं। मैं जवाब –

+0

संपादित कर दूंगा, मैं इस पर स्पष्ट नहीं था; उसके लिए खेद है। मैंने इसका जिक्र किया। –

संबंधित मुद्दे