2011-01-20 6 views
6

के लिए नीचे दिए गए कोड नमूना की जांच करें:अप्रत्याशित व्यवहार ThreadPool.QueueUserWorkItem

public class Sample 
{ 
    public int counter { get; set; } 
    public string ID; 
    public void RunCount() 
    { 
     for (int i = 0; i < counter; i++) 
     { 
      Thread.Sleep(1000); 

      Console.WriteLine(this.ID + " : " + i.ToString()); 
     } 
    } 
} 

class Test 
{ 
    static void Main() 
    { 
     Sample[] arrSample = new Sample[4]; 

     for (int i = 0; i < arrSample.Length; i++) 
     { 
      arrSample[i] = new Sample(); 
      arrSample[i].ID = "Sample-" + i.ToString(); 
      arrSample[i].counter = 10; 
     } 

     foreach (Sample s in arrSample) 
     { 
      ThreadPool.QueueUserWorkItem(callback => s.RunCount()); 
     } 

     Console.ReadKey(); 
    } 

} 

इस नमूने के लिए उम्मीद उत्पादन होना चाहिए कुछ की तरह:

Sample-0 : 0 
Sample-1 : 0 
Sample-2 : 0 
Sample-3 : 0 
Sample-0 : 1 
Sample-1 : 1 
Sample-2 : 1 
Sample-3 : 1 
. 
. 
. 

हालांकि, जब आप इस कोड को चलाने के लिए, यह इसके बदले कुछ ऐसा दिखाएगा:

Sample-3 : 0 
Sample-3 : 0 
Sample-3 : 0 
Sample-3 : 1 
Sample-3 : 1 
Sample-3 : 0 
Sample-3 : 2 
Sample-3 : 2 
Sample-3 : 1 
Sample-3 : 1 
. 
. 
. 

मैं समझ सकता हूं कि जिस क्रम में था विज्ञापन निष्पादित हो सकते हैं अलग-अलग हो सकते हैं और इसलिए राउंड रॉबिन फैशन में गिनती बढ़ रही नहीं है। हालांकि, मैं समझने के लिए, क्यों सभी ID रों, Sample-3 के रूप में प्रदर्शित किया जा रहा है, जबकि निष्पादन स्पष्ट रूप से एक दूसरे से स्वतंत्र हो रहा है असफल।

Arent विभिन्न वस्तुओं अलग धागे के साथ प्रयोग किया जा रहा है?

+0

एक और दिन, एक और व्यक्ति को जानते हुए भी कैसे ठीक बंद उपयोग करने के लिए नहीं:


इस मामले में एक अन्य विकल्प मुद्दा कुछ भी कब्जा नहीं द्वारा पूरी तरह से चकमा है। कोई आश्चर्य नहीं कि जावा उन्हें जोड़ने के लिए इतना अनिच्छुक है ... – leppie

+1

क्या leppie कह रहा है कि आप इसके मूल्य के बजाय चर '' को कैप्चर करते हैं। जब तक धागे निष्पादित होते हैं, पुनरावृत्ति की जाती है और 's' को इसके अंतिम मूल्य के साथ छोड़ दिया जाता है। – Zarat

+0

मुझे लगता है कि मैं यहां खो गया हूं .. क्लोजर समस्या बीटीडब्ल्यू –

उत्तर

10

इस वर्ष संशोधित बंद समस्या है। आप इस मुद्दे को समझने के लिए: Threadpools - possible thread execution order problem पर एक समान प्रश्न के लिए देख सकते हैं, और एरिक लिपर्ट के ब्लॉग पोस्ट Closing over the loop variable considered harmful पर विचार करना चाहते हैं।

अनिवार्य रूप से, लैम्ब्डा अभिव्यक्ति आपने चरs बिंदु लैम्ब्डा घोषित किया जाता है पर चर के मूल्य से वहाँ कैप्चर कर रहा है बल्कि मिला है। नतीजतन, परिवर्तनीय के मान में किए गए बाद के परिवर्तन प्रतिनिधि के लिए दृश्यमान हैं। Sample का उदाहरण जिस पर RunCount विधि चलती है आवृत्ति पर निर्भर करता है जो कि s (इसके मान) द्वारा संदर्भित बिंदु पर प्रतिनिधि वास्तव में निष्पादित करता है।

इसके अतिरिक्त, के बाद से प्रतिनिधि (रों) (संकलक वास्तव में एक ही प्रतिनिधि उदाहरण पुनः उपयोग कर लेता) में अतुल्यकालिक निष्पादित किया जा रहा है, यह गारंटी नहीं है कि क्या ये मान प्रत्येक निष्पादन के बिंदु पर हो जाएगा। (- यह धागा पूल पर कार्य शेड्यूल करने के लिए समय लगता है उम्मीद की जा करने के लिए) क्या आप वर्तमान में देख रहे हैं कि foreach पाश मुख्य धागा पर पूरा करता प्रतिनिधि-आमंत्रण से किसी से पहले है। तो सभी कार्य-वस्तुएं लूप-चर के 'अंतिम' मान को सिलाई करती हैं। लेकिन यह किसी भी माध्यम से गारंटी नहीं है; लूप के अंदर उचित अवधि Thread.Sleep डालने का प्रयास करें, और आप एक अलग आउटपुट देखेंगे।

  1. अन्य चर अंदर पाश शरीर परिचय दें:


    सामान्य ठीक है।

  2. पाश-चर के वर्तमान मूल्य है कि चर असाइन करें।
  3. कैद 'प्रतिलिपि' चर के बजाय लैम्ब्डा के अंदर पाश-चर।

    foreach (Sample s in arrSample) 
    { 
        Sample sCopy = s; 
        ThreadPool.QueueUserWorkItem(callback => sCopy.RunCount()); 
    } 
    

अब प्रत्येक काम के आइटम "मालिक" पाश चर की एक विशेष मूल्य।

ThreadPool.QueueUserWorkItem(obj => ((Sample)obj).RunCount(), s); 
+1

अरघ, दुखी पत्नी ने बुलाया! लेकिन महान दिमाग एक जैसे सोचते हैं :) – leppie

+0

धन्यवाद @ani, यह समझ में आता है। कोई आश्चर्य नहीं कि मैं फ्लेम हो गया! मुझे यह पता होना चाहिए .. :) –