2010-02-12 9 views
7

मेरे पास एक उदाहरण है कि जब भी मैं एक इटरेटर का उपयोग करता हूं तो मैं ब्रेक कर सकता हूं, लेकिन यह लूप के साथ ठीक काम करता है। सभी कोड निष्पादन विधि के लिए स्थानीय चर का उपयोग करता है। मैं फंस गया हूँ या तो इटरेटर के बारे में एक तथ्य है कि मुझे पता नहीं है, या नेट में भलाई बग के लिए ईमानदार है। मैं पूर्व पर सट्टेबाजी कर रहा हूँ। कृपया मदद करें।इस कोड में एक पुनरावर्तक (.Net) अविश्वसनीय क्यों होगा

यह कोड विश्वसनीय रूप से हर बार काम करता है। यह किसी भी समय एक तत्व के रूप में नए धागे के पूर्णांक को पार करते हुए, एक समय में सभी तत्वों को एक बार में छोड़ देता है और एक नया धागा शुरू करता है। यह 10 धागे शुरू करता है, प्रत्येक आइटम के लिए एक। 1,2,3,4,5,6,7,8,9,10 - यह हमेशा काम करता है।

काम कोड:

//lstDMSID is a populated List<int> with 10 elements. 
for(int i=0; i<lstDMSID.Count; i++) 
{ 
    int dmsId = lstDMSID[i]; 
    ThreadStart ts = delegate 
    { 
     // Perform some isolated work with the integer 
     DoThreadWork(dmsId); 
    }; 
    Thread thr = new Thread(ts); 
    thr.Name = dmsId.ToString(); 
    thr.Start(); 
} 

और यह कोड वास्तव में तत्वों को दोहराएँ। यह एक बार में सभी तत्वों के माध्यम से (चलो 10 कहें) और एक नया धागा शुरू करता है। यह 10 धागे शुरू करता है, लेकिन यह विश्वसनीय रूप से सभी 10 पूर्णांक प्राप्त नहीं करता है। मुझे लगता है कि यह 1,2,3,3,6,7,7,8,910 शुरू हो रहा है। मैं संख्या खो रहा हूँ।

पर्दाफाश कोड:

//lstDMSID is a populated List<int> with 10 elements. 
foreach(int dmsId in lstDMSID) 
{ 
    ThreadStart ts = delegate 
    { 
     // Perform some isolated work with the integer 
     DoThreadWork(dmsId); 
    }; 
    Thread thr = new Thread(ts); 
    thr.Name = dmsId.ToString(); 
    thr.Start(); 
} 
+0

, बार-बार पूर्णांकों हमेशा नहीं कर रहे हैं वही। यह यादृच्छिक प्रतीत होता है। –

उत्तर

12

समस्या अपने दायरे के लिए उत्पन्न बंद पर आधारित है ...

एक ही समस्या आपके लिए पाश में क्या होगा, थे आप यह इतना तरह के पुनर्लेखन के लिए (खराब कोड!):

// ...Closure now happens at this scope... 
for(int i=0;i<lstDMSID.Count;i++) 
{ 
    ThreadStart ts = delegate 
    { 
     DoThreadWork(lstDMSID[i]); // Eliminate the temporary, and it breaks! 
    }; 
    Thread thr = new Thread(ts); 
    thr.Name = dmsId.ToString(); 
    thr.Start(); 
} 

समस्या यह है कि, जब आप एक प्रतिनिधि (एक मामले में, dmsId) में एक चर को बंद करते हैं, तो बंद होने पर वैरिएबल घोषित किया जा रहा है। जब आप फॉरवर्ड या फोरैच लूप का उपयोग करते हैं, तो बंद/फोरैच स्टेटमेंट के दायरे में बंद होता है, जो एक स्तर बहुत अधिक होता है।

एक अस्थायी चर अंदर foreach पाश का परिचय समस्या को दूर करेंगे:

foreach(int dmsId in lstDMSID) 
{ 
    int temp = dmsId; // Add temporary 
    ThreadStart ts = delegate 
    { 
     DoThreadWork(temp); // close over temporary, and it's fixed 
    }; 
    Thread thr = new Thread(ts); 
    thr.Name = dmsId.ToString(); 
    thr.Start(); 
} 

क्या हो रहा है की एक अधिक विस्तृत चर्चा के लिए, मैं Eric Lippert's blog post: "Closing over the loop variable considered harmful" पढ़ने की सलाह देते हैं।

+0

@ रीड कॉपसी - ग्रेट स्पष्टीकरण और महान संदर्भ लेख। उत्तरों की संख्या के आधार पर, शायद मुझे यह पता होना चाहिए था। मुझे अब पता है! –

+0

@Jride: यह कुछ डेवलपर्स याद आती है, क्योंकि यह तब तक एक मुद्दा प्रतीत नहीं होता जब तक आप थ्रेडिंग (अधिकांश समय) लागू नहीं करते। मैं हमेशा इसी कारण से, मेरी समांतरता वार्ता में इसका उल्लेख करता हूं। –

1

पहले मामले में, dmsId पाश के लिए के दायरे के भीतर घोषित किया जाता है, प्रत्येक प्रतिनिधि अपने आप ही उस चर के "उदाहरण" कैप्चर करता है।

दूसरे संस्करण में, डीएमएसआईडी को फ़ोरैच लूप के पूरे दायरे के लिए घोषित किया गया है। प्रत्येक प्रतिनिधि एक ही वैरिएबल को कैप्चर करता है - जिसका मतलब है कि आप एक ही वैरिएबल को कई थ्रेड से एक्सेस कर रहे हैं जिसमें लॉकिंग नहीं है - खराब चीजें हो सकती हैं।

3

यह उस चर के कारण है जो आप बंद करने में उपयोग कर रहे हैं।

एरिक Lippert है विस्तार से एक nice blog post explaining this, और मुझे लगता है कि दूसरों के (जॉन स्कीट?) इसके बारे में भी ब्लॉग किया है।

3

अपने foreach में अज्ञात विधि करते समय, संकलक मूल रूप से एक कक्षा उत्पन्न करता है जो dmsId को इंगित करता है। इसलिए, जैसे धागे शुरू होते हैं, प्रत्येक एक ही चर पर इंगित कर रहा है, इसलिए जब थ्रेड निर्धारित होते हैं, तो आप डुप्लीकेट या छोड़े जाने वाले नंबर देखेंगे।

पाश के लिए है, तो आप पूर्णांक की एक प्रति बना रहे हैं तो प्रत्येक थ्रेड अपने स्वयं के मूल्य हो जाता है।

इस समस्या पर कुछ अच्छा डेटा here है।

2

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

यह इसे ठीक करना चाहिए:

//lstDMSID is a populated List with 10 elements. 
foreach(int dmsId in lstDMSID) 
{ 
    int tempId = dmsId; 
    ThreadStart ts = delegate 
    { 
     //this is method that goes off ad does some isolated work with the integer 
     DoThreadWork(tempId); 
    }; 
    Thread thr = new Thread(ts); 
    thr.Name = tempId.ToString(); 
    thr.Start(); 
} 
एक नोट के रूप
+0

वह अभी भी टूट जाएगा - यह मूल रूप से समस्या के समान है। पेश किए गए चर को फ़ोरैच लूप के अंदर स्कॉप्ड किया जाना चाहिए। फिर बंद हो जाएगा ठीक से। –

+0

बेहतर;) यह तय है। –

+0

नहीं, मैंने उत्तर लिखने से पहले गलती से सबमिट किया। यह अब दिखाए गए काम करता है। – Gabe

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