System.Threading.Timer in C # ऐसा लगता है कि काम नहीं कर रहा है। यह हर 3 सेकंड में बहुत तेज चलता है


112

मेरे पास एक टाइमर ऑब्जेक्ट है। मैं चाहता हूं कि इसे हर मिनट चलाया जाए। विशेष रूप से, यह एक OnCallBackविधि चलाना चाहिए और एक OnCallBackविधि चल रही है, जबकि निष्क्रिय हो जाता है। एक बार एक OnCallBackविधि समाप्त होने के बाद, यह (ए OnCallBack) टाइमर को पुनरारंभ करता है।

यहाँ मेरे पास अभी है:

private static Timer timer;

private static void Main()
{
    timer = new Timer(_ => OnCallBack(), null, 0, 1000 * 10); //every 10 seconds
    Console.ReadLine();
}

private static void OnCallBack()
{
    timer.Change(Timeout.Infinite, Timeout.Infinite); //stops the timer
    Thread.Sleep(3000); //doing some long operation
    timer.Change(0, 1000 * 10);  //restarts the timer
}

हालांकि, यह काम नहीं कर रहा है। यह हर 3 सेकंड में बहुत तेज चलता है। भले ही एक अवधि (1000 * 10) बढ़ा दें। ऐसा लगता है जैसे यह एक आँख बंद कर लेता है1000 * 10

मैंने गलत क्या किया?


12
से Timer.Change: "यदि देयता शून्य (0) है, तो कॉलबैक विधि तुरंत लागू की जाती है।" ऐसा लगता है कि यह मेरे लिए शून्य है।
डेमियन_इन_यूएनबेलिएवर

2
हाँ, लेकिन ऐसा क्या? एक अवधि भी है।
एलन कोरोमेनो

10
तो क्या होगा अगर एक अवधि भी है? उद्धृत वाक्य अवधि मूल्य के बारे में कोई दावा नहीं करता है। यह सिर्फ कहता है "यदि यह मान शून्य है, तो मैं तुरंत कॉलबैक को लागू करने जा रहा हूं"।
डेमियन___बेलिवर

3
दिलचस्प बात यह है कि अगर आप नियत समय और अवधि दोनों को 0 पर सेट करते हैं, तो टाइमर हर सेकंड चलेगा और तुरंत शुरू होगा।
केल्विन

जवाबों:


230

यह System.Threading.Timer का सही उपयोग नहीं है। जब आप टाइमर को तुरंत बंद कर देते हैं, तो आपको लगभग हमेशा निम्न करना चाहिए:

_timer = new Timer( Callback, null, TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );

यह टाइमर को केवल एक बार टिक करने का निर्देश देगा जब अंतराल समाप्त हो गया है। फिर अपने कॉलबैक फ़ंक्शन में आप टाइमर को एक बार काम पूरा होने के बाद बदलते हैं, इससे पहले नहीं। उदाहरण:

private void Callback( Object state )
{
    // Long running operation
   _timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );
}

इस प्रकार कोई लॉकिंग तंत्र की आवश्यकता नहीं है क्योंकि कोई संगति नहीं है। अगले अंतराल के लंबे समय तक चलने के संचालन के समय के बाद टाइमर अगले कॉलबैक को आग लगा देगा।

यदि आपको अपने टाइमर को बिल्कुल एन मिलीसेकंड पर चलाने की आवश्यकता है, तो मेरा सुझाव है कि आप स्टॉपवॉच का उपयोग करके लंबे समय तक चलने वाले ऑपरेशन के समय को मापें और फिर बदलें विधि को उचित रूप से कॉल करें:

private void Callback( Object state )
{
   Stopwatch watch = new Stopwatch();

   watch.Start();
   // Long running operation

   _timer.Change( Math.Max( 0, TIME_INTERVAL_IN_MILLISECONDS - watch.ElapsedMilliseconds ), Timeout.Infinite );
}

मैं .NET करने वाले किसी व्यक्ति को दृढ़ता से प्रोत्साहित करता हूं और सीएलआर का उपयोग कर रहा हूं जिसने जेफरी रिक्टर की पुस्तक - सीएलआर को सी # के माध्यम से नहीं पढ़ा है, जितनी जल्दी हो सके पढ़ने के लिए। टाइमर और थ्रेड पूल को वहां महान विवरणों में समझाया गया है।


6
मैं इससे सहमत नहीं हूं private void Callback( Object state ) { // Long running operation _timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite ); }Callbackएक ऑपरेशन पूरा होने से पहले फिर से बुलाया जा सकता है।
एलन कोरोमेनो

2
मेरा मतलब था कि Long running operationतब और अधिक समय लग सकता है TIME_INTERVAL_IN_MILLISECONDS। फिर क्या होगा?
एलन कोरोमेनो

32
कॉलबैक फिर से नहीं बुलाया जाएगा, यह बात है। यही कारण है कि हम Timeout.Infinite को दूसरे पैरामीटर के रूप में पास करते हैं। यह मूल रूप से टाइमर के लिए फिर से टिक नहीं करता है। फिर हमने ऑपरेशन पूरा करने के बाद टिक करने के लिए पुनर्निर्धारित किया।
इवान ज़्लातानोव

नौसिखिया यहां फैलने के लिए - क्या आपको लगता है ThreadPoolकि टाइमर में पास होने पर ऐसा करना संभव है ? मैं एक ऐसे परिदृश्य के बारे में सोच रहा हूं, जहां एक नया धागा एक निश्चित अंतराल पर नौकरी करने के लिए पैदा होता है - और फिर पूरा होने पर थ्रेड पूल पर फिर से लगाया जाता है।
jedd.ahyoung

2
System.Threading.Timer एक थ्रेड पूल टाइमर है, जो थ्रेड पूल पर कॉलबैक को निष्पादित कर रहा है, न कि एक समर्पित थ्रेड। टाइमर कॉलबैक रूटीन को पूरा करने के बाद, कॉलबैक निष्पादित करने वाला थ्रेड वापस पूल में चला जाता है।
इवान ज़्लातनोव

14

टाइमर को रोकना आवश्यक नहीं है, इस पोस्ट से अच्छा समाधान देखें :

"आप टाइमर को कॉलबैक विधि को जारी रखने दे सकते हैं, लेकिन अपने गैर-रीएंन्ट्रेंट कोड को मॉनीटर.ट्री.इन्टर / एक्जिट में लपेट सकते हैं। उस स्थिति में टाइमर को रोकने / फिर से शुरू करने की कोई आवश्यकता नहीं है; ओवरलैपिंग कॉल लॉक को अधिग्रहित नहीं करेगी और तुरंत वापस आ जाएगी।"

private void CreatorLoop(object state) 
 {
   if (Monitor.TryEnter(lockObject))
   {
     try
     {
       // Work here
     }
     finally
     {
       Monitor.Exit(lockObject);
     }
   }
 }

यह मेरे मामले के लिए नहीं है। मुझे टाइमर बिल्कुल बंद करने की आवश्यकता है।
एलन कोरोमेनो

क्या आप एक बार फिर कॉलबैक दर्ज करने से रोकने की कोशिश कर रहे हैं? यदि नहीं, तो आप क्या हासिल करना चाहते हैं?
इवान लियोनेंको

1. कॉलबैक में एक बार से अधिक दर्ज करना रोकें। 2. बहुत बार निष्पादन को रोकें।
एलन कोरोमोनो

यह ठीक यही करता है। # 2 ज्यादा ओवरहेड नहीं है जब तक कि अगर वस्तु लॉक है तो स्टेटमेंट के ठीक बाद वापस आती है, खासकर यदि आपके पास इतना बड़ा अंतराल है।
इवान लियोनेंको

1
यह गारंटी नहीं देता है कि कोड को अंतिम अंतराल के बाद <अंतराल> से कम नहीं कहा जाता है (पिछले टिक जारी होने के बाद टाइमर के एक नए टिक को माइक्रोसेकंड से निकाल दिया जा सकता है)। यह निर्भर करता है कि यह एक सख्त आवश्यकता है या नहीं (समस्या के विवरण से पूरी तरह से स्पष्ट नहीं)।
मार्को Mp

9

System.Threading.Timerअनिवार्य उपयोग कर रहा है ?

यदि नहीं, तो System.Timers.Timerकाम Start()और Stop()तरीके हैं (और एक AutoResetसंपत्ति जिसे आप गलत सेट कर सकते हैं, ताकि Stop()ज़रूरत न हो और आप बस Start()निष्पादित करने के बाद कॉल करें )।


3
हां, लेकिन यह एक वास्तविक आवश्यकता हो सकती है, या यह सिर्फ इसलिए हुआ कि टाइमर को चुना गया था क्योंकि यह सबसे अधिक उपयोग किया जाने वाला है। दुख की बात है कि .NET के टन वस्तुएं हैं, 90% के लिए अतिव्यापी लेकिन फिर भी (कभी-कभी सूक्ष्म रूप से) अलग। बेशक, अगर यह एक आवश्यकता है, तो यह समाधान बिल्कुल भी लागू नहीं होता है।
मार्को Mp

2
प्रलेखन के अनुसार : System.Timer वर्ग केवल .NET फ्रेमवर्क में उपलब्ध है। यह .NET मानक लाइब्रेरी में शामिल नहीं है और अन्य प्लेटफार्मों पर उपलब्ध नहीं है, जैसे .NET कोर या यूनिवर्सल विंडोज प्लेटफॉर्म। इन प्लेटफार्मों पर, साथ ही सभी .NET प्लेटफार्मों में पोर्टेबिलिटी के लिए, आपको इसके बजाय System.Threading.Timer वर्ग का उपयोग करना चाहिए।
NotAgain का कहना है कि मोनिका

3

मैं बस करूँगा:

private static Timer timer;
 private static void Main()
 {
   timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds
   Console.ReadLine();
 }

  private static void OnCallBack()
  {
    timer.Dispose();
    Thread.Sleep(3000); //doing some long operation
    timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds
  }

और पीरियड पैरामीटर को नजरअंदाज करें, क्योंकि आप पीरियड्स को खुद कंट्रोल करने की कोशिश कर रहे हैं।


आपका मूल कोड यथासंभव तेज़ चल रहा है, क्योंकि आप पैरामीटर के 0लिए निर्दिष्ट करते रहते हैं dueTime। से Timer.Change:

यदि समयावधि शून्य (0) है, तो कॉलबैक विधि तुरंत लागू की जाती है।


2
क्या टाइमर को निपटाना आवश्यक है? आप Change()विधि का उपयोग क्यों नहीं करते ?
एलन कोरोमनो

21
हर बार टाइमर का निपटान करना बिल्कुल अनावश्यक और गलत है।
इवान ज़्लाटनोव

0
 var span = TimeSpan.FromMinutes(2);
 var t = Task.Factory.StartNew(async delegate / () =>
   {
        this.SomeAsync();
        await Task.Delay(span, source.Token);
  }, source.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);

source.Cancel(true/or not);

// or use ThreadPool(whit defaul options thread) like this
Task.Start(()=>{...}), source.Token)

अगर यू अंदर कुछ लूप धागे का उपयोग करें ...

public async void RunForestRun(CancellationToken token)
{
  var t = await Task.Factory.StartNew(async delegate
   {
       while (true)
       {
           await Task.Delay(TimeSpan.FromSeconds(1), token)
                 .ContinueWith(task => { Console.WriteLine("End delay"); });
           this.PrintConsole(1);
        }
    }, token) // drop thread options to default values;
}

// And somewhere there
source.Cancel();
//or
token.ThrowIfCancellationRequested(); // try/ catch block requred.
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.