2010-08-06 50 views
13

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

मैंने अपनी सेवा में टाइमर ऑब्जेक्ट बनाया है, जो हर 60 सेकंड में टिकता है और वांछित विधि का आह्वान करता है।
चूंकि मैं नहीं चाहता कि यह टाइमर नई लाइनों को संसाधित करते समय टिक टिके, मैंने विधि को lock { } ब्लॉक में लपेट लिया, इसलिए यह किसी अन्य थ्रेड द्वारा पहुंचा नहीं जा सकेगा। अब

Timer serviceTimer = new Timer(); 
serviceTimer.Interval = 60; 
serviceTimer.Elapsed += new ElapsedEventHandler(serviceTimer_Elapsed); 
serviceTimer.Start(); 

void serviceTimer_Elapsed(object sender, ElapsedEventArgs e) 
{ 
    lock (this) 
    { 
     // do some heavy processing... 
    } 
} 

, मैं सोच रहा हूँ -
मेरी टाइमर टिक्स हैं, और डाटाबेस पर नई पंक्तियाँ का एक बहुत पाता है, और अब प्रसंस्करण से अधिक ले जाएगा:

यह कुछ इस तरह दिखता 60 सेकंड, अगली टिक पिछले एक तक समाप्त होने तक कोई प्रसंस्करण नहीं करेगी। यह वह प्रभाव है जो मैं चाहता हूं।

लेकिन अब, पहली प्रक्रिया समाप्त होने के बाद सेवा टिमर_इलेस्ड विधि तत्काल बंद हो जाएगी, या फिर टाइमर को फिर से टिकने की प्रतीक्षा होगी।

मैं क्या करना चाहता हूं - अगर प्रसंस्करण 60 सेकंड से अधिक की आवश्यकता है, टाइमर की तुलना में थ्रेड लॉक हो जाएगा, और फिर से जांचने के लिए 60 सेकंड प्रतीक्षा करें, इसलिए मैं ऐसी परिस्थिति में कभी भी अटक जाऊंगा पिछले एक के लिए इंतजार कर रहे धागे की एक कतार।

मैं इस परिणाम को कैसे पूरा कर सकता हूं?
ऐसा करने के लिए सबसे अच्छा अभ्यास क्या है?

धन्यवाद!

उत्तर

19

आप

// Just in case someone wants to inherit your class and lock it as well ... 
private static object _padlock = new object(); 
try 
{ 
    serviceTimer.Stop(); 

    lock (_padlock) 
    { 
     // do some heavy processing... 
    } 
} 
finally 
{ 
    serviceTimer.Start(); 
} 

संपादित तरह प्रसंस्करण के दौरान टाइमर, कुछ अक्षम करने का प्रयास हो सकता है: ओपी निर्दिष्ट नहीं किया है कि क्या reentrancy केवल टाइमर द्वारा या सेवा बहु लड़ी था कारण होता था। बाद में मान लिया है, लेकिन अगर टाइमर बंद हो जाता है (ऑटोरसेट या मैन्युअल रूप से)

+11

का प्रयोग न करें 'ताला (यह)' - http: // stackoverflow.com/questions/251391/why-is-lockthis-bad –

+3

ऑटोरेसेट को झूठी पर सेट करना आसान होगा, तो हमें कुछ भी लॉक करने की आवश्यकता नहीं है – javapowered

7

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

Timer serviceTimer = new Timer(); 
serviceTimer.Interval = 60; 
serviceTimer.Elapsed += new ElapsedEventHandler(serviceTimer_Elapsed); 
serviceTimer.Start(); 
bool isRunning = false; 
void serviceTimer_Elapsed(object sender, ElapsedEventArgs e) 
{ 
    lock (this) 
    { 
     if(isRunning) 
      return; 
     isRunning = true; 
    } 
    try 
    { 
    // do some heavy processing... 
    } 
    finally 
    { 
     isRunning = false; 
    } 
} 
+1

मुझे अपने से अधिक nonnb का समाधान पसंद है लेकिन मैं इसे छोड़ दूंगा एक उदाहरण प्रदान करें जब आपके पास फायरिंग से ईवेंट को रोकने की क्षमता न हो। –

+0

इसके अलावा, घटना को फायरिंग जारी रखने की अनुमति कुछ संदर्भों में उपयोगी होगी। आप इसे लॉग कर सकते हैं या मुख्य प्रक्रिया के लिए बाहरी कुछ अन्य प्रोसेसिंग कर सकते हैं। –

2

अन्य विकल्प एक BackgroundWorker वर्ग, या TheadPool.QueueUserWorkItem उपयोग करने के लिए हो सकता है।

पृष्ठभूमि कार्यकर्ता आपको अभी भी वर्तमान प्रसंस्करण के लिए विकल्प जांच देगा और एक समय में 1 आइटम को संसाधित करेगा। थ्रेडपूल आपको बैकग्राउंड थ्रेड के लिए हर टिक (यदि आवश्यक हो) कतारों को जारी रखने की क्षमता प्रदान करेगा।

आपके विवरण से, मुझे लगता है कि आप किसी डेटाबेस में कतार में आइटम की जांच कर रहे हैं। इस मामले में, मैं पृष्ठभूमि को काम को धक्का देने के लिए थ्रेडपूल का उपयोग करता हूं, और आपकी जांच तंत्र को धीमा/बंद नहीं करता।

एक सेवा के लिए, मैं वास्तव में सुझाव देता हूं कि आप थ्रेडपूल दृष्टिकोण का उपयोग करके देखेंगे। इस तरह, आप अपने टाइमर के साथ हर 60 सेकंड में नए आइटम की जांच कर सकते हैं, फिर उन्हें कतार दें, और नेट को पता लगाएं कि प्रत्येक आइटम को कितना आवंटित करना है, और केवल आइटम को कतार में धक्का देना जारी रखें।

उदाहरण के लिए: यदि आप केवल टाइमर का उपयोग करते हैं और आपके पास 5 नई पंक्तियां हैं, जिन्हें 65 सेकंड प्रोसेसिंग समय की आवश्यकता होती है। थ्रेडपूल दृष्टिकोण का उपयोग करके, यह 5 पृष्ठभूमि कार्य वस्तुओं के साथ 65 सेकंड में किया जाएगा। टाइमर दृष्टिकोण का उपयोग करके, इसमें 4+ मिनट लगेंगे (जिस मिनट में आप प्रत्येक पंक्ति के बीच प्रतीक्षा करेंगे), इसके साथ ही यह कतार में आने वाले अन्य कार्यों का बैक-लॉग भी हो सकता है।

Timer serviceTimer = new Timer(); 
    void startTimer() 
    { 
     serviceTimer.Interval = 60; 
     serviceTimer.Elapsed += new ElapsedEventHandler(serviceTimer_Elapsed); 
     serviceTimer.AutoReset = false; 
     serviceTimer.Start(); 
    } 
    void serviceTimer_Elapsed(object sender, ElapsedEventArgs e) 
    { 
     try 
     { 
      // Get your rows of queued work requests 

      // Now Push Each Row to Background Thread Processing 
      foreach (Row aRow in RowsOfRequests) 
      { 
       ThreadPool.QueueUserWorkItem(
        new WaitCallback(longWorkingCode), 
        aRow); 
      } 
     } 
     finally 
     { 
      // Wait Another 60 Seconds and check again 
      serviceTimer.Stop(); 
     } 
    } 

    void longWorkingCode(object workObject) 
    { 
     Row workRow = workObject as Row; 
     if (workRow == null) 
      return; 

     // Do your Long work here on workRow 
    } 
19

आप इस मामले में ताला की जरूरत नहीं है:

यह इस तरह से किया जाना चाहिए का एक उदाहरण है। Timer.AutoReset = इसे शुरू करने से पहले झूठी सेट करें। अपनी प्रसंस्करण के साथ किए जाने के बाद हैंडलर में टाइमर को पुनरारंभ करें। यह सुनिश्चित करेगा कि टाइमर 60 सेकंड प्रत्येक कार्य के बाद आग लगाता है।

+2

+1 मुझे कभी पता नहीं था कि ऑटोरेसेट – Searock

4

मैं आपको सलाह देता हूं कि आप इसकी प्रसंस्करण के दौरान टाइमर को टिक न दें।

टाइमर ऑटोरसेट को झूठी पर सेट करें। और अंत में इसे शुरू करें। यहाँ एक पूरा जवाब आप Needed: A Windows Service That Executes Jobs from a Job Queue in a DB; Wanted: Example Code

0

में रुचि हो सकती है एक और posibility कुछ इस तरह किया जाएगा है:

void serviceTimer_Elapsed(object sender, ElapsedEventArgs e) 
{ 
    if (System.Threading.Monitor.IsLocked(yourLockingObject)) 
     return; 
    else 
     lock (yourLockingObject) 
     // your logic 
      ; 
} 
5

अन्य उत्तर पर ऐसा ही एक बदलाव है, कि टाइमर टिक टिक रखने के लिए अनुमति देता है और केवल काम जब करना टाइमर को रोकने के बजाय लॉक प्राप्त किया जा सकता है।

बीता ईवेंट हैंडलर में इस रखो:

if (Monitor.TryEnter(locker) 
{ 
    try 
    { 
     // Do your work here. 
    } 
    finally 
    { 
     Monitor.Exit(locker); 
    } 
} 
+0

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

2

काफी प्रतिक्रियाशील एक्सटेंशन के साथ इस के हल के लिए एक साफ रास्ता नहीं है। यहाँ कोड है, और आप यहां पूरा विवरण पढ़ सकते हैं: http://www.zerobugbuild.com/?p=259

public static IDisposable ScheduleRecurringAction(
    this IScheduler scheduler, 
    TimeSpan interval, 
    Action action) 
{ 
    return scheduler.Schedule(
     interval, scheduleNext => 
    { 
     action(); 
     scheduleNext(interval); 
    }); 
} 

और तुम इस तरह इसका इस्तेमाल कर सकते हैं:

TimeSpan interval = TimeSpan.FromSeconds(5); 
Action work =() => Console.WriteLine("Doing some work..."); 

var schedule = Scheduler.Default.ScheduleRecurringAction(interval, work);   

Console.WriteLine("Press return to stop."); 
Console.ReadLine(); 
schedule.Dispose(); 
+2

यह एक बहुत अच्छा समाधान है और आरएक्स शेड्यूलर का एक दिलचस्प उपयोग दिखाता है जो शायद तुरंत स्पष्ट नहीं है। – jamesmus

+0

इस समाधान ने मेरे लिए अच्छा काम किया। मुझे शेड्यूलर में भी एक नई अंतर्दृष्टि दी – jumpercake

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

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