विंडोज सेवा में उपयोग करने के लिए सर्वश्रेष्ठ टाइमर


108

मुझे कुछ विंडोज़ सेवा बनाने की ज़रूरत है जो हर एन अवधि को निष्पादित करेगी।
प्रश्न है:
मुझे किस टाइमर नियंत्रण का उपयोग करना चाहिए: System.Timers.Timerया System.Threading.Timerएक? क्या यह किसी चीज पर प्रभाव डालता है?

मैं पूछ रहा हूं क्योंकि मैंने System.Timers.Timerविंडोज़ सेवाओं में गैर-सही काम के लिए कई सबूतों को सुना है ।
धन्यवाद।

जवाबों:


118

दोनों System.Timers.Timerऔर System.Threading.Timerसेवाओं के लिए काम करेंगे।

टाइमर आप बचना चाहते हैं कर रहे हैं System.Web.UI.Timerऔर System.Windows.Forms.Timerहै, जो एएसपी अनुप्रयोगों और WinForms के लिए क्रमशः रहे हैं। उन का उपयोग करने से सेवा को एक अतिरिक्त असेंबली लोड करने का कारण होगा जो आपके द्वारा बनाए जा रहे एप्लिकेशन के प्रकार के लिए वास्तव में आवश्यक नहीं है।

System.Timers.Timerनिम्नलिखित उदाहरण की तरह उपयोग करें (यह भी, सुनिश्चित करें कि आप कचरा संग्रह को रोकने के लिए एक वर्ग स्तर चर का उपयोग करते हैं, जैसा कि टिम रॉबिन्सन के उत्तर में कहा गया है:

using System;
using System.Timers;

public class Timer1
{
    private static System.Timers.Timer aTimer;

    public static void Main()
    {
        // Normally, the timer is declared at the class level,
        // so that it stays in scope as long as it is needed.
        // If the timer is declared in a long-running method,  
        // KeepAlive must be used to prevent the JIT compiler 
        // from allowing aggressive garbage collection to occur 
        // before the method ends. (See end of method.)
        //System.Timers.Timer aTimer;

        // Create a timer with a ten second interval.
        aTimer = new System.Timers.Timer(10000);

        // Hook up the Elapsed event for the timer.
        aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);

        // Set the Interval to 2 seconds (2000 milliseconds).
        aTimer.Interval = 2000;
        aTimer.Enabled = true;

        Console.WriteLine("Press the Enter key to exit the program.");
        Console.ReadLine();

        // If the timer is declared in a long-running method, use
        // KeepAlive to prevent garbage collection from occurring
        // before the method ends.
        //GC.KeepAlive(aTimer);
    }

    // Specify what you want to happen when the Elapsed event is 
    // raised.
    private static void OnTimedEvent(object source, ElapsedEventArgs e)
    {
        Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
    }
}

/* This code example produces output similar to the following:

Press the Enter key to exit the program.
The Elapsed event was raised at 5/20/2007 8:42:27 PM
The Elapsed event was raised at 5/20/2007 8:42:29 PM
The Elapsed event was raised at 5/20/2007 8:42:31 PM
...
 */

यदि आप चुनते हैं System.Threading.Timer, तो आप निम्नानुसार उपयोग कर सकते हैं:

using System;
using System.Threading;

class TimerExample
{
    static void Main()
    {
        AutoResetEvent autoEvent     = new AutoResetEvent(false);
        StatusChecker  statusChecker = new StatusChecker(10);

        // Create the delegate that invokes methods for the timer.
        TimerCallback timerDelegate = 
            new TimerCallback(statusChecker.CheckStatus);

        // Create a timer that signals the delegate to invoke 
        // CheckStatus after one second, and every 1/4 second 
        // thereafter.
        Console.WriteLine("{0} Creating timer.\n", 
            DateTime.Now.ToString("h:mm:ss.fff"));
        Timer stateTimer = 
                new Timer(timerDelegate, autoEvent, 1000, 250);

        // When autoEvent signals, change the period to every 
        // 1/2 second.
        autoEvent.WaitOne(5000, false);
        stateTimer.Change(0, 500);
        Console.WriteLine("\nChanging period.\n");

        // When autoEvent signals the second time, dispose of 
        // the timer.
        autoEvent.WaitOne(5000, false);
        stateTimer.Dispose();
        Console.WriteLine("\nDestroying timer.");
    }
}

class StatusChecker
{
    int invokeCount, maxCount;

    public StatusChecker(int count)
    {
        invokeCount  = 0;
        maxCount = count;
    }

    // This method is called by the timer delegate.
    public void CheckStatus(Object stateInfo)
    {
        AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
        Console.WriteLine("{0} Checking status {1,2}.", 
            DateTime.Now.ToString("h:mm:ss.fff"), 
            (++invokeCount).ToString());

        if(invokeCount == maxCount)
        {
            // Reset the counter and signal Main.
            invokeCount  = 0;
            autoEvent.Set();
        }
    }
}

दोनों उदाहरण MSDN पृष्ठों से आते हैं।


1
आप सुझाव क्यों देते हैं: GC.KeepAlive (aTimer) ?, aTimer उदाहरण चर सही है, इसलिए उदाहरण के लिए यदि यह उदाहरण चर है, तब तक हमेशा इसका संदर्भ रहेगा जब तक कि रूप है, है न?
जियोर्जिअम

37

इसके लिए किसी सेवा का उपयोग न करें। एक सामान्य एप्लिकेशन बनाएं और इसे चलाने के लिए एक निर्धारित कार्य बनाएं।

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

"यदि आप एक Windows सेवा लिख ​​रहे हैं जो टाइमर चलाता है, तो आपको अपने समाधान का पुनर्मूल्यांकन करना चाहिए।"

-जॉन गैलोवे, ASP.NET MVC सामुदायिक कार्यक्रम प्रबंधक, लेखक, अंशकालिक सुपर हीरो


26
यदि आपकी सेवा पूरे दिन चलने का इरादा है, तो शायद एक सेवा निर्धारित कार्य के बजाय समझ में आती है। या, एक सेवा होने से व्यवस्थापक को आसान बना सकता है और एक अवसंरचना समूह के लिए लॉगिंग कर सकता है जो एप्लिकेशन टीम की तरह समझदार नहीं है। हालांकि, इस धारणा पर सवाल उठाना कि एक सेवा की आवश्यकता पूरी तरह से वैध है, और ये नकारात्मक रेटिंग अवांछनीय हैं। दोनों को +1।
सूफूका जूल

2
@ एमआर बकवास। शेड्यूल किए गए कार्य OS का एक मुख्य हिस्सा हैं। कृपया अपने FUD को अपने पास रखें।

4
@MR: मैं एक इंजीलवादी नहीं हूँ, मैं एक यथार्थवादी हूँ। और वास्तविकता ने मुझे सिखाया है कि अनुसूचित कार्य "बहुत छोटी गाड़ी" नहीं हैं। यह वास्तव में, उस व्यक्ति के रूप में है जिसने इसे समर्थन करने का दावा किया है। अन्यथा, आप जो कुछ भी कर रहे हैं वह भय, अनिश्चितता और संदेह फैला रहा है।

13
मुझे यहां पर पनाह देने से नफरत है, लेकिन मुझे एमआर का थोड़ा बचाव करना होगा। मेरी कंपनी में कई महत्वपूर्ण एप्लिकेशन चल रहे हैं जो विंडोज़ कंसोल ऐप हैं। मैंने उन सभी को चलाने के लिए विंडोज टास्क शेड्यूलर का उपयोग किया है। अब कम से कम 5 मौकों पर, हमें एक ऐसी समस्या हुई है, जिसमें शेड्यूलर सर्विस किसी तरह "भ्रमित" हो गई। कार्य निष्पादित नहीं हुए और कुछ एक अजीब स्थिति में थे। एकमात्र समाधान सर्वर का एक रिबूट या शेड्यूलर सेवा को रोकना और शुरू करना था। ऐसा कुछ नहीं जो मैं बिना प्रशासनिक अधिकारों के कर सकता हूं और किसी उत्पादन परिवेश में स्वीकार्य नहीं हूं। बस मेरी $ 0.02।
SpaceCowboy74

6
यह वास्तव में मेरे साथ कुछ सर्वरों पर हुआ है (3 अब तक)। हालांकि यह मानदंड नहीं होगा। बस कभी-कभी यह कहना कि कुछ करने की अपनी पद्धति को लागू करना बुरा नहीं है।
SpaceCowboy74

7

या तो किसी को ठीक काम करना चाहिए। वास्तव में, System.Threading.Timer आंतरिक रूप से System.Timers.Timer का उपयोग करता है।

ऐसा कहने के बाद, System.Timers.Timer का दुरुपयोग करना आसान है। यदि आप टाइमर ऑब्जेक्ट को किसी चर में स्टोर नहीं करते हैं, तो यह कचरा एकत्र करने के लिए उत्तरदायी है। यदि ऐसा होता है, तो आपके टाइमर में आग नहीं लगेगी। टाइमर को रोकने के लिए डिस्पोज़ विधि को कॉल करें, या System.Threading.Timer वर्ग का उपयोग करें, जो कि थोड़ा सा अच्छा आवरण है।

आपने अब तक क्या समस्याएं देखी हैं?


मुझे आश्चर्य होता है कि Windows Phone ऐप केवल System.Threading.Timer का उपयोग क्यों कर सकता है।
स्टोनटिप

विंडोज़ फोन में संभवत: फ्रेमवर्क का हल्का संस्करण होता है, जिसके अतिरिक्त सभी कोड दोनों विधियों का उपयोग करने में सक्षम होते हैं, शायद इसकी आवश्यकता नहीं होती है इसलिए यह शामिल नहीं है। मुझे लगता है कि निक का जवाब, इस बात का एक बेहतर कारण है कि विंडोज फोन की पहुंच क्यों नहीं है System.Timers.Timerक्योंकि यह उस पर फेंके गए अपवादों को संभाल नहीं पाएगा।
मलाची

2

मैं पिछली टिप्पणी से सहमत हूं कि एक अलग दृष्टिकोण पर विचार करना सबसे अच्छा हो सकता है। मेरा सुझाव एक सांत्वना आवेदन लिखना होगा और विंडोज़ अनुसूचक का उपयोग करना होगा:

यह करेगा:

  • नलसाजी कोड को कम करें जो अनुसूचक व्यवहार को दोहराता है
  • शेड्यूलिंग व्यवहार (जैसे कि केवल सप्ताहांत पर चलने वाले) के संदर्भ में अधिक लचीलापन प्रदान करें, जिसमें सभी शेड्यूलिंग लॉजिक एप्लिकेशन कोड से अलग हों
  • कॉन्फ़िगरेशन फ़ाइलों आदि में कॉन्फ़िगरेशन कॉन्फ़िगरेशन सेटअप मानों के बिना पैरामीटर के लिए कमांड लाइन तर्कों का उपयोग करें
  • विकास के दौरान डिबग / परीक्षण करना बहुत आसान है
  • किसी सहायता उपयोगकर्ता को कंसोल एप्लिकेशन को सीधे लागू करके निष्पादित करने की अनुमति दें (जैसे समर्थन स्थितियों के दौरान उपयोगी)

3
लेकिन इसके लिए उपयोगकर्ता पर लॉग की आवश्यकता है? तो सेवा शायद बेहतर है अगर यह एक सर्वर पर 24/7 चलेगा।
जेपी हेल्लेमन्स 10

1

जैसा कि पहले ही कहा गया है System.Threading.Timerऔर दोनों System.Timers.Timerकाम करेंगे। दोनों के बीच बड़ा अंतर यह है कि System.Threading.Timerएक आवरण दूसरे को घेरे रहता है।

System.Threading.TimerSystem.Timers.Timerसभी अपवादों को निगलने के दौरान अधिक अपवाद हैंडलिंग होगी।

इसने मुझे अतीत में बड़ी समस्याएं दीं, इसलिए मैं हमेशा 'System.Threading.Timer' का उपयोग करता और अभी भी अपने अपवादों को बहुत अच्छी तरह से संभालता।


0

मुझे पता है कि यह धागा थोड़ा पुराना है, लेकिन यह मेरे पास एक विशिष्ट परिदृश्य के लिए काम में आया था और मुझे लगा कि यह ध्यान देने योग्य है कि एक और कारण है कि System.Threading.Timerएक अच्छा दृष्टिकोण क्यों हो सकता है। जब आपको समय-समय पर एक नौकरी का निष्पादन करना होता है, जिसमें लंबा समय लग सकता है और आप यह सुनिश्चित करना चाहते हैं कि पूरी प्रतीक्षा अवधि का उपयोग नौकरियों के बीच किया जाए या यदि आप नहीं चाहते हैं कि पिछली नौकरी खत्म होने से पहले नौकरी फिर से चले तो कार्य टाइमर अवधि से अधिक समय लेता है। आप निम्नलिखित का उपयोग कर सकते हैं:

using System;
using System.ServiceProcess;
using System.Threading;

    public partial class TimerExampleService : ServiceBase
    {
        private AutoResetEvent AutoEventInstance { get; set; }
        private StatusChecker StatusCheckerInstance { get; set; }
        private Timer StateTimer { get; set; }
        public int TimerInterval { get; set; }

        public CaseIndexingService()
        {
            InitializeComponent();
            TimerInterval = 300000;
        }

        protected override void OnStart(string[] args)
        {
            AutoEventInstance = new AutoResetEvent(false);
            StatusCheckerInstance = new StatusChecker();

            // Create the delegate that invokes methods for the timer.
            TimerCallback timerDelegate =
                new TimerCallback(StatusCheckerInstance.CheckStatus);

            // Create a timer that signals the delegate to invoke 
            // 1.CheckStatus immediately, 
            // 2.Wait until the job is finished,
            // 3.then wait 5 minutes before executing again. 
            // 4.Repeat from point 2.
            Console.WriteLine("{0} Creating timer.\n",
                DateTime.Now.ToString("h:mm:ss.fff"));
            //Start Immediately but don't run again.
            StateTimer = new Timer(timerDelegate, AutoEventInstance, 0, Timeout.Infinite);
            while (StateTimer != null)
            {
                //Wait until the job is done
                AutoEventInstance.WaitOne();
                //Wait for 5 minutes before starting the job again.
                StateTimer.Change(TimerInterval, Timeout.Infinite);
            }
            //If the Job somehow takes longer than 5 minutes to complete then it wont matter because we will always wait another 5 minutes before running again.
        }

        protected override void OnStop()
        {
            StateTimer.Dispose();
        }
    }

    class StatusChecker
        {

            public StatusChecker()
            {
            }

            // This method is called by the timer delegate.
            public void CheckStatus(Object stateInfo)
            {
                AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
                Console.WriteLine("{0} Start Checking status.",
                    DateTime.Now.ToString("h:mm:ss.fff"));
                //This job takes time to run. For example purposes, I put a delay in here.
                int milliseconds = 5000;
                Thread.Sleep(milliseconds);
                //Job is now done running and the timer can now be reset to wait for the next interval
                Console.WriteLine("{0} Done Checking status.",
                    DateTime.Now.ToString("h:mm:ss.fff"));
                autoEvent.Set();
            }
        }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.