क्या सी # टाइमर एक अलग धागे पर समाप्त होता है?


97

क्या System.Timers.Timer उस थ्रेड से अलग थ्रेड पर है जो इसे बनाता है?

कहते हैं कि मेरे पास टाइमर के साथ एक क्लास है जो हर 5 सेकंड में फायर करता है। जब टाइमर आग लग जाती है, तो बीती हुई विधि में, किसी वस्तु को संशोधित किया जाता है। कहते हैं कि 10 सेकंड की तरह, इस ऑब्जेक्ट को संशोधित करने में लंबा समय लगता है। क्या यह संभव है कि मैं इस परिदृश्य में धागा टकराव में भाग जाऊंगा?


इससे समस्याएं पैदा हो सकती हैं। ध्यान दें कि, सामान्य रूप से, थ्रेडपूल थ्रेड लंबे समय से चलने वाली प्रक्रियाओं के लिए डिज़ाइन नहीं किए गए हैं।
ग्रेग डी।

मैं इस में चल रहा था, एक विंडोज़ सेवा के साथ परीक्षण। मेरे लिए जो काम किया गया, वह टाइमर को OnTimer ईवेंट में पहले निर्देश के रूप में अक्षम कर रहा था, मेरे कार्यों को निष्पादित कर रहा था, फिर अंत में टाइमर को सक्षम कर रहा था। यह कुछ समय के लिए उत्पादन वातावरण में मज़बूती से काम किया है।
स्टीव

जवाबों:


60

के लिए System.Timers.Timer :

देखें नीचे ब्रायन गिदोन के जवाब

के लिए System.Threading.Timer :

टाइमर पर MSDN प्रलेखन :

System.Threading.Timer क्लास थ्रेडपूल थ्रेड पर कॉलबैक बनाता है और इवेंट मॉडल का उपयोग बिल्कुल नहीं करता है।

तो वास्तव में टाइमर एक अलग धागे पर समाप्त होता है।


21
सच है, लेकिन यह एक पूरी तरह से अलग वर्ग है। ओपी ने System.Timers.Timer वर्ग के बारे में पूछा।
ब्रायन गिदोन

1
ओह, तुम सही हो। msdn.microsoft.com/en-us/library/system.timers.timer.aspx का कहना है कि "बीता हुआ ईवेंट थ्रेडपूल थ्रेड पर उठाया गया है।" मैं वहाँ से ही निष्कर्ष निकालता हूं।
जोरन

6
ठीक है, हाँ, लेकिन यह इतना आसान नहीं है। मेरा जवाब देखिए।
ब्रायन गिदोन

192

निर्भर करता है। System.Timers.Timerऑपरेशन के दो स्वरूप हैं।

यदि SynchronizingObjectएक ISynchronizeInvokeउदाहरण के लिए सेट किया गया है, तो Elapsedघटना सिंक्रनाइज़ ऑब्जेक्ट को होस्ट करने वाले थ्रेड पर निष्पादित होगी। आमतौर पर ये ISynchronizeInvokeउदाहरण सादे पुराने Controlऔर Formउदाहरणों के अलावा और कोई नहीं हैं जिनसे हम सभी परिचित हैं। तो उस स्थिति में Elapsedघटना को UI थ्रेड पर लागू किया जाता है और यह समान व्यवहार करता है System.Windows.Forms.Timer। अन्यथा, यह वास्तव में उस विशिष्ट ISynchronizeInvokeउदाहरण पर निर्भर करता है जिसका उपयोग किया गया था।

यदि SynchronizingObjectअशक्त है तो Elapsedघटना को एक ThreadPoolसूत्र में पिरोया जाता है और यह समान व्यवहार करता है System.Threading.Timer। वास्तव में, यह वास्तव System.Threading.Timerमें पर्दे के पीछे का उपयोग करता है और जरूरत पड़ने पर टाइमर कॉलबैक प्राप्त करने के बाद मार्शलिंग ऑपरेशन करता है।


4
आप एक नया धागा पर अमल करने के लिए टाइमर कॉलबैक चाहते हैं तो आपके का उपयोग करना चाहिए एक System.Threading.Timerया System.Timers.Timer?
CJ7

1
@ cj7: कोई भी ऐसा कर सकता है।
ब्रायन गिदोन

और अगर मेरे पास जटिल प्रकार (व्यक्ति) की सूची है और प्रत्येक व्यक्ति के अंदर एक समय रखना चाहते हैं? मुझे इसे उसी धागे (सभी व्यक्तियों) पर चलाने की आवश्यकता है, क्योंकि अगर यह पहले व्यक्ति विधि को बुलाता है, तो दूसरे व्यक्ति को पहले समाप्त होने वाली घटना के समाप्त होने तक इंतजार करना चाहिए। क्या मै वह कर सकता हूं?
लिएंड्रो डी मेलो फागुंडेस

4
सभी ... System.Timers.Timerऑपरेशन के दो तरीके हैं। यह बेतरतीब ढंग से असाइन किए गए थ्रेड पूल थ्रेड पर चल सकता है या जो भी थ्रेड होस्ट कर रहा है उस पर चल सकता है ISynchronizeInvoke। मुझे नहीं पता कि कैसे और अधिक स्पष्ट करना है। System.Threading.Timerमूल प्रश्न के साथ थोड़ा (यदि कुछ भी) करना है।
ब्रायन गिदोन

@LeandroDeMelloFagundes क्या आप उसके लिए उपयोग नहीं कर सकते lock?
ओजंक

24

प्रत्येक बीता हुआ ईवेंट उसी थ्रेड में तब तक फायर करेगा जब तक कि पिछला एल्प्सड अभी भी चल रहा हो।

तो यह आपके लिए टकराव को संभालता है

इसे कंसोल में रखने का प्रयास करें

static void Main(string[] args)
{
    Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
    var timer = new Timer(1000);
    timer.Elapsed += timer_Elapsed;
    timer.Start();
    Console.ReadLine();
}

static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
    Thread.Sleep(2000);
    Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
}

आपको ऐसा कुछ मिलेगा

10
6
12
6
12

जहां 10 बुला धागा है और 6 और 12 बीजी बीता हुआ घटना से फायरिंग कर रहे हैं। यदि आप थ्रेड को हटा देते हैं। सो (2000); आपको ऐसा कुछ मिलेगा

10
6
6
6
6

चूंकि कोई टकराव नहीं हैं।

लेकिन यह अभी भी एक समस्या के साथ यू छोड़ देता है। अगर आप हर 5 सेकंड में इवेंट फायर कर रहे हैं और यू एडिट करने के लिए 10 सेकंड लगते हैं तो कुछ एडिटिंग को छोड़ना होगा।


8
timer.Stop()Elapsed इवेंट विधि की शुरुआत में जोड़ना और फिर Elapsed ईवेंट विधि timer.Start()के अंत में Elapsed ईवेंट को टकराने से रखेंगे।
मेट्रो स्मर्फ़

4
आपको टाइमर लगाने की आवश्यकता नहीं है। स्वप्न () आपको केवल टाइमर को परिभाषित करने की आवश्यकता है। तब आप टाइमर बनाते हैं। घटना को संसाधित करने के बाद। मुझे लगता है कि यह एक बेहतर तरीका है जिससे आप टकराव से बचते हैं।
जोओ एंटन्स

17

System.Timers.Timer के लिए, अलग-अलग थ्रेड पर, अगर SynchronizingObject सेट नहीं है।

    static System.Timers.Timer DummyTimer = null;

    static void Main(string[] args)
    {
        try
        {

            Console.WriteLine("Main Thread Id: " + System.Threading.Thread.CurrentThread.ManagedThreadId);

            DummyTimer = new System.Timers.Timer(1000 * 5); // 5 sec interval
            DummyTimer.Enabled = true;
            DummyTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnDummyTimerFired);
            DummyTimer.AutoReset = true;

            DummyTimer.Start();

            Console.WriteLine("Hit any key to exit");
            Console.ReadLine();
        }
        catch (Exception Ex)
        {
            Console.WriteLine(Ex.Message);
        }

        return;
    }

    static void OnDummyTimerFired(object Sender, System.Timers.ElapsedEventArgs e)
    {
        Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
        return;
    }

यदि आप DummyTimer को 5 सेकंड के अंतराल पर निकालते हैं, तो आउटपुट देखें:

Main Thread Id: 9
   12
   12
   12
   12
   12
   ... 

इसलिए, जैसा कि देखा गया है, OnDummyTimerFired को वर्कर्स थ्रेड पर निष्पादित किया जाता है।

नहीं, आगे की जटिलता - यदि आप 10 एमएस कहने के लिए अंतराल को कम करते हैं,

Main Thread Id: 9
   11
   13
   12
   22
   17
   ... 

ऐसा इसलिए होता है क्योंकि यदि अगले टिक को निकाल दिए जाने पर OnDummyTimerFired का प्रचलित निष्पादन नहीं किया जाता है, तो .NET इस काम को करने के लिए एक नया थ्रेड तैयार करेगा।

आगे चीजों की शिकायत करते हुए, "System.Timers.Timer वर्ग इस दुविधा से निपटने का एक आसान तरीका प्रदान करता है - यह एक सार्वजनिक सिंक्रोनाइज़िंगऑब्जेक्ट प्रॉपर्टी को उजागर करता है। इस प्रॉपर्टी को विंडोज फॉर्म (या विंडोज फॉर्म पर एक नियंत्रण) की स्थापना सुनिश्चित करेगा। आपके एलैप किए गए ईवेंट हैंडलर का कोड उसी थ्रेड पर चलता है, जिस पर SynchronizingObject का त्वरित मूल्यांकन किया गया था। "

http://msdn.microsoft.com/en-us/magazine/cc164015.aspx#S2


यह एक अच्छा उदाहरण है। मुझे अब तक नहीं पता था और मुझे यहां और वहां कुछ ताले लगाने की जरूरत है। उत्तम।
ओलारू मिरेसा

और क्या होगा यदि मैं चाहता हूं कि टाइमर कंसोल एप्लिकेशन के मुख्य धागे में एलाप्स घटना को आग लगा दे?
जैकट्री

13

यदि बीती हुई घटना अधिक समय लेती है, तो अंतराल, यह बीता हुआ घटना को बढ़ाने के लिए एक और धागा पैदा करेगा। लेकिन इसके लिए वर्कअराउंड है

static void timer_Elapsed(object sender, ElapsedEventArgs e)    
{     
   try
   {
      timer.Stop(); 
      Thread.Sleep(2000);        
      Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);    
   }
   finally
   {
     timer.Start();
   }
}

+1 सरल और स्वच्छ उत्तर, लेकिन यह हमेशा काम नहीं करेगा। बल्कि किसी को टाइमर की लॉक या सिंक्रोनाइज़िंगऑब्जेक्ट का उपयोग करना चाहिए।
रोलरकोस्टा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.