2009-04-28 15 views
5

मैं क्या मुझे लगता है एक बहुत आम सूत्रण परिदृश्य है:थ्रेड कतार

  • मैं पूरा करने के लिए
  • सभी नौकरियों प्रत्येक अन्य
  • मैं कार्रवाई करने के लिए चाहते हैं पर निर्भर नहीं हैं 100 समान नौकरी एक समय
  • हर काम पूर्ण कर देती है पर 15 नौकरियों की एक अधिकतम, एक नया काम शुरू कर दिया किया जाएगा जब तक सभी नौकरियों
  • पूरी हो चुकी हैं

यदि आप मानते हैं कि प्रत्येक कार्य पूरा होने पर एक घटना को आग लग जाएगा (मैं पृष्ठभूमिवर्कर वर्ग का उपयोग कर रहा हूं), मैं इसे खींचने के कुछ तरीकों के बारे में सोच सकता हूं, लेकिन मुझे यकीन नहीं है कि " सही "समाधान है। मैं उम्मीद कर रहा था कि आप में से कुछ गुरुओं को सही दिशा में इंगित कर सकते हैं।

समाधान 1:है (जारी रखें) {थ्रेडिंग। नींद (1000); } मेरे मुख्य() फ़ंक्शन में लूप। Job_Completed ईवेंट हैंडलर में कोड जारी रहेगा = झूठी ए) कोई नौकरियां कतारबद्ध नहीं रहती हैं और बी) सभी कतारबद्ध नौकरियां पूरी हो चुकी हैं। मैंने पहले इस समाधान का उपयोग किया है और ऐसा लगता है कि यह ठीक काम करता है ... यह मेरे लिए थोड़ा "अजीब" लगता है।

समाधान 2: मेरे मुख्य() फ़ंक्शन में एप्लिकेशन का उपयोग करें।()। इसी तरह, Job_Completed ईवेंट हैंडलर में कोड एप्लिकेशन.एक्सिट() को कॉल करेगा जब ए) कोई नौकरियां कतारबद्ध नहीं रहेंगी और बी) सभी कतारबद्ध नौकरियां पूरी हो चुकी हैं।

समाधान 3: एक ThreadPool का उपयोग करें, सभी 500-1000 अनुरोध अप कतार, उन्हें एक समय (SetMaxThreads) में 10 चलाने के लिए और किसी भी तरह पूरा करने के लिए उन सब को के लिए प्रतीक्षा करते हैं।

इन सभी समाधानों में, मूल विचार यह है कि जब तक कोई नौकरी नहीं छोड़ी जाती, तब तक एक और नौकरी पूरी होने पर एक नई नौकरी शुरू की जाएगी। इसलिए, समस्या न केवल मौजूदा नौकरियों को पूरा करने की प्रतीक्षा कर रही है, बल्कि तब तक प्रतीक्षा कर रही है जब तक कि कोई लंबित नौकरियां शुरू नहीं हो जातीं। यदि थ्रेडपूल सही समाधान है, तो सभी कतारबद्ध वस्तुओं को पूरा करने के लिए थ्रेडपूल पर प्रतीक्षा करने का सही तरीका क्या है?

मुझे लगता है कि मेरा ओवरराइडिंग भ्रम यहां है कि मुझे समझ में नहीं आता कि कैसे मेरे मुख्य() फ़ंक्शन के भीतर से ईवेंट कैसे आग लगने में सक्षम हैं। जाहिर है, वे करते हैं, मैं बस विंडोज मैसेज लूप पॉइंट-ऑफ-व्यू से मैकेनिक्स को समझ नहीं पा रहा हूं। इस समस्या को हल करने का सही तरीका क्या है, और क्यों?

+0

यह थ्रेडपूल शैली समाधान के आसपास सबसे अधिक सुझाव केंद्र जैसा लगता है ... मेरे प्रस्तावित समाधान 1 और समाधान 2 के बारे में क्या है (मूल रूप से निकाली जाने वाली किसी घटना द्वारा संशोधित होने वाली स्थिति पर प्रतीक्षा करना)? क्या ऐसा कुछ भी करने में स्वाभाविक रूप से गलत है, या यह है कि .NET थ्रेडपूल प्रदान करता है, इसलिए यह आवश्यक नहीं है? ऐसा लगता है कि इस तरह कोड होना अजीब लगता है: जबकि (जारी रखें) थ्रेडिंग। नींद (1000); ... मुख्य() फ़ंक्शन में ईवेंट को आग लगने का इंतजार करते समय। इस तरह के कोड में ... जब मेरी घटनाओं को ठीक से संभाला जाता है ... कहीं सोते हैं() कॉल? –

उत्तर

0

यहाँ कैसे मैं इसे दृष्टिकोण होगा की स्यूडोकोड (इस ThreadPool का लाभ उठाने नहीं है, इसलिए किसी को एक बेहतर जवाब :)

main 
{ 
    create queue of 100 jobs 
    create new array of 15 threads 
    start threads, passing each the job queue 
    do whatever until threads are done 
} 

thread(queue) 
{ 
    while(queue isn't empty) 
    { 
     lock(queue) { if queue still isn't empty dequeue a thing } 
     process the thing 
    } 

    queue is empty so exit thread 
} 

संपादित हो सकता है: आपकी समस्या बताने के लिए जब धागे हैं, तो समाप्त हो गया है, और आप सामान्य सी # थ्रेड का उपयोग कर रहे हैं, (थ्रेडपूलेड थ्रेड्स नहीं) आप एक थ्रेड पर थ्रेड.जॉइन() को प्रत्येक थ्रेड पर वैकल्पिक टाइमआउट के साथ कॉल कर सकते हैं और थ्रेड होने के बाद ही यह वापस आ जाएगा।आप इस तरह से एक तरह से नज़र रखने के लिए सिर्फ कितने धागे से एक पर लटका दिया हो रही बिना किया जाता है, तो आप उन्हें एक के बाद एक कर सकते हैं चाहते हैं:

for(int i = 0; allThreads.Count > 0; i++) 
{ 
    var thisThread = allThreads[i % threads.Count]; 
    if(thisThread.Join(timeout)) // something low, maybe 100 ms or something 
     allThreads.Remove(thisThread); 
} 
2

पुन: "पूरा करने के लिए किसी भी तरह उन सब के लिए प्रतीक्षा"

ManualResetEvent आपका मित्र है, इससे पहले कि आप अपना बड़ा बैच शुरू करें, इन पिल्लों में से एक बनाएं, अपने मुख्य धागे में प्रतीक्षा करें, जब काम पूरा हो जाए तो पृष्ठभूमि ऑपरेशन के अंत में इसे सेट करें।

एक अन्य विकल्प, thread.Join() मैन्युअल रूप से धागे बना सकते हैं और एक foreach धागा करना है

आप इस्तेमाल कर सकते हैं इस (मैं परीक्षण के दौरान इस का उपयोग)

 private void Repeat(int times, int asyncThreads, Action action, Action done) { 
     if (asyncThreads > 0) { 

      var threads = new List<Thread>(); 

      for (int i = 0; i < asyncThreads; i++) { 

       int iterations = times/asyncThreads; 
       if (i == 0) { 
        iterations += times % asyncThreads;      
       } 

       Thread thread = new Thread(new ThreadStart(() => Repeat(iterations, 0, action, null))); 
       thread.Start(); 
       threads.Add(thread); 
      } 

      foreach (var thread in threads) { 
       thread.Join(); 
      } 

     } else { 
      for (int i = 0; i < times; i++) { 
       action(); 
      } 
     } 
     if (done != null) { 
      done(); 
     } 
    } 

उपयोग:

// Do something 100 times in 15 background threads, wait for them all to finish. 
Repeat(100, 15, DoSomething, null) 
1

मैं केवल कार्य समानांतर लाइब्रेरी का उपयोग करूंगा।

आप इसे एक एकल, सरल समांतर के रूप में कर सकते हैं। अपने कार्यों के साथ लूप के लिए, और यह स्वचालित रूप से इसे साफ़ रूप से प्रबंधित करेगा। यदि आप सी # 4 और माइक्रोसॉफ्ट के कार्यान्वयन की प्रतीक्षा नहीं कर सकते हैं, तो अस्थायी कामकाज सिर्फ संकलित करना और Mono Implementation of TPL का उपयोग करना है। (मैं व्यक्तिगत रूप से एमएस कार्यान्वयन, विशेष रूप से नए बीटा रिलीज को प्राथमिकता देता हूं, लेकिन मोनो एक कार्यात्मक और पुनर्वितरण योग्य है।)

0

जब आप थ्रेड कतार में कोई कार्य आइटम कतार करते हैं, तो आपको एक वाइंडंडल वापस लेना चाहिए। उन्हें सभी को सरणी में रखें और आप इसे WaitAll() फ़ंक्शन के लिए तर्क के रूप में पास कर सकते हैं।

+0

अच्छा विचार है, लेकिन आप यह कैसे करेंगे? QueueUserWorkItem एक बूल देता है। – Grokys

1

मैं थ्रेडपूल का उपयोग करूंगा।

अपनी नौकरी चलाने शुरू करने से पहले, ManualResetEvent और एक इंट काउंटर बनाएं। प्रत्येक नौकरी को थ्रेडपूल में जोड़ें, प्रत्येक बार काउंटर को बढ़ाएं।

प्रत्येक नौकरी के अंत में, काउंटर को कम करें और जब यह शून्य हो जाए, तो ईवेंट पर Set() पर कॉल करें।

अपने मुख्य धागे में, सभी नौकरियों को पूरा करने के लिए WaitOne() पर कॉल करें।

3

भले ही अन्य उत्तर अच्छे हैं यदि आप एक और विकल्प चाहते हैं (आपके पास पर्याप्त विकल्प नहीं हो सकते हैं), तो इसके बारे में एक विचार के रूप में कैसे।

बस प्रत्येक नौकरी के लिए एक संरचना में डेटा डालें, जो फीफो स्टैक में है।

15 धागे बनाएं।

प्रत्येक धागे को स्टैक से अगली नौकरी मिल जाएगी, इसे बंद कर दिया जाएगा।

जब कोई धागा प्रसंस्करण को समाप्त करता है, तो अगली नौकरी पाएं, अगर स्टैक खाली है तो थ्रेड मर जाता है या बस सो जाता है।

एकमात्र जटिलता, जो हल करने के लिए बहुत आसान है, पॉपिंग एक महत्वपूर्ण खंड (पढ़ने/पॉप सिंक्रनाइज़) में हो रही है।

0

ThreadPool जाने का रास्ता हो सकता है। SetMaxThreads विधि निष्पादित किए जा रहे थ्रेड की संख्या को प्रतिबंधित करने में सक्षम होगी। हालांकि, यह प्रक्रिया/AppDomain के लिए अधिकतम थ्रेड को प्रतिबंधित करता है।यदि प्रक्रिया एक सेवा के रूप में चल रही है तो मैं SetMaxThreads का उपयोग करने का सुझाव नहीं दूंगा।

private static ManualResetEvent manual = new ManualResetEvent(false); 
private static int count = 0; 

public void RunJobs(List<JobState> states) 
{ 
    ThreadPool.SetMaxThreads(15, 15); 

    foreach(var state in states) 
    { 
      Interlocked.Increment(count); 
      ThreadPool.QueueUserWorkItem(Job, state); 
    } 

    manual.WaitOne(); 
} 

private static void Job(object state) 
{ 
    // run job 
    Interlocked.Decrement(count); 
    if(Interlocked.Read(count) == 0) manual.Set(); 
} 
0

माइक्रोसॉफ्ट के रिएक्टिव फ्रेमवर्क इस के लिए शानदार है:

Action[] jobs = new Action[100]; 

var subscription = 
    jobs 
     .ToObservable() 
     .Select(job => Observable.Start(job)) 
     .Merge(15) 
     .Subscribe(
      x => Console.WriteLine("Job Done."), 
      () => Console.WriteLine("All Jobs Done.")) 

हो गया।

बस NuGet "System.Reactive"।

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