2012-01-17 4 views
12

मेरे पास एक ऐसा एप्लिकेशन है जहां मैं एकाधिक धागे को एक सूची पढ़ने के लिए चाहता हूं। मैं समय-समय पर नए डेटा के साथ सूची अद्यतन करना चाहता हूं। जब सूची अपडेट की जाती है, तो मुझे लगता है कि मैं एक नई सूची बना सकता हूं और इसे पुराने के साथ बदल सकता हूं। उदाहरण:सी # मल्टीथ्रेडेड अनुप्रयोग में परिवर्तनीय संदर्भ को स्वैप करते समय लॉकिंग आवश्यक है

private List<string> _list = new List<string>(); 
private void UpdateList() 
{ 
    var newList = new List<string>(QueryList(...)); 
    _list = newList; 
} 

private void ThreadRun() 
{ 
    foreach (var item in _list) 
    { 
     // process item... 
    } 
} 

अद्यतन सूची विधि में, एक नई सूची बनाई गई है और _list संदर्भ नई सूची के साथ बदल दिया गया है। मेरी सोच से, कोई भी मौजूदा धागा अभी भी पुरानी सूची (जो मेरे लिए ठीक है) का संदर्भ रखेगा, कोई भी नया धागा नई सूची उठाएगा। आखिरकार, सभी धागे खत्म हो जाएंगे और पुरानी सूची अंततः कचरा एकत्र की जाएगी। क्या इस कोड में कोई लॉकिंग आवश्यक है, या क्या मुझे कुछ भी सुरक्षित बहु-थ्रेडेड एक्सेस सुनिश्चित करने के लिए देखभाल करने की आवश्यकता है?

+0

'क्वेरी लिस्ट' क्या करता है? '_list' के तत्व कभी भी एक अलग थ्रेड (जोड़ा या हटाया गया) द्वारा संशोधित हैं? – Groo

+0

dQueryList बस स्ट्रिंग का संग्रह देता है। मैंने जानबूझकर दिखाया कि एक नई सूची बनाई गई है, यह दिखाने के लिए कि सूची कभी मोहित नहीं हुई है। – Mas

उत्तर

6

यह सुनिश्चित करने के लिए कि न तो स्थिरता और न ही ऑप्टिमाइज़ेशन आपको चोट पहुंचाते हैं, फ़ील्ड को अपडेट करने के लिए Interlocked.Exchange() का उपयोग करें। फिर आपके पढ़ने पर volatile होने के खर्च के बिना, लिखने पर उचित मेमोरी बाधा होगी।

+0

मैंने Interlocked.Exchange() का उपयोग नहीं किया है। क्या आप कह रहे हैं कि यह अस्थिर उपयोग करने से तेज है? – Mas

+0

क्या आप इसे थोड़ा सा विस्तारित कर सकते हैं? जहां तक ​​मुझे पता है, सभी 'इंटरलाक्ड' विधियां (पूर्ण) स्मृति बाधा उत्पन्न करती हैं। दूसरे शब्दों में, वे धीमे हैं, लेकिन 'अस्थिर' से सुरक्षित हैं। – Groo

+1

@ ग्रू वे अस्थिर के लिए लिखने से धीमे हैं, लेकिन जब से इंटरलॉक और सादे रीड्स द्वारा लिखे गए परिदृश्य में लिखने से अधिक पढ़ा जाता है, तो प्रत्येक पढ़ने और लिखने के बजाय अस्थिर होना चाहिए। –

3

असाइनमेंट परमाणु है, आपका कोड ठीक है। आप _list के रूप में अस्थिर हालांकि अंकन है कि किसी भी धागे कि चर का अनुरोध सबसे नवीनतम संस्करण के लिए उठो सुनिश्चित करने के लिए विचार करना चाह सकते: सूची अपरिवर्तनीय के उदाहरण

private volatile List<string> _list = new List<string>(); 
4

आप कर रहे हैं और यह केवल अस्थायी स्थिति है कि समस्याओं का कारण बनता है।

आपका कोड ठीक है, लेकिन आपको _list को volatile के रूप में चिह्नित करने पर विचार करना चाहिए। इसका मतलब यह होगा कि सभी पढ़े नवीनतम संस्करण प्राप्त करते हैं और कंपाइलर/जिटर/सीपीयू किसी भी पठन को अनुकूलित नहीं करेगा।

वैकल्पिक रूप से, आप यह सुनिश्चित करने के लिए नई सूची निर्दिष्ट करने से पहले Thread.MemoryBarrier() डाल सकते हैं कि नई सूची में सभी लिखने से पहले आप इसे प्रकाशित करने से पहले प्रतिबद्ध हैं, लेकिन यह x86 आर्किटेक्चर पर कोई समस्या नहीं है।

+0

यदि यह अस्थिर है, तो स्मृति बाधा क्या है? –

+0

मुझे नहीं लगता कि इस मामले में _list को अस्थिर होने की आवश्यकता है, क्योंकि इसे थ्रेडरुन() की शुरुआत में एक बार पढ़ा जाता है, अगर इसे लूप में पढ़ा गया था और नया मान चाहता था, तो उसे अस्थिर होना चाहिए इसे कंपाइलर द्वारा एक रजिस्टर में कैश किए जाने से रोकें। –

+0

@ JonHanna, IanRingrose आपकी टिप्पणियों के लिए धन्यवाद - मैंने अपना जवाब अपडेट कर दिया है। –

3

आप .NET 4 का उपयोग कर या + आप नए नए धागा सुरक्षित संग्रह का उपयोग कर सकते हैं, तो ...

BlockingCollection<string> list = new BlockingCollection<string>(); 

BlockingCollection वर्ग बल्कि निर्माता उपभोक्ता की तरह जोड़ने और, सदस्यों को हटाने के लिए धागे की सुरक्षित विधियां डिज़ाइन पैटर्न।

थ्रेड जोड़ सकते हैं और अन्य धागे कोई प्रोग्रामिंग ओवरहेड वाले सूची सदस्यों को हटा सकते हैं।

इसके अतिरिक्त, यह आप इस तरह काम करने के लिए अनुमति देता है ...

foreach (string i in list) 
    { 
     list.Take(); 
     list.Add(i + 200); 
    } 

इस कोड को दूर करता है और जबकि इसकी प्रगणक काम कर रहा है एक संग्रह करने के लिए कहते हैं, कुछ है जो कभी नेट से पहले सी # में किया जा सकता है 4. इसे अस्थिर घोषित करने की कोई अतिरिक्त आवश्यकता नहीं है।

foreach (string i in list) 
    { 
     new Task(() =>list.Take()).Start(); 
     new Task(() =>list.Add(i + 200)).Start(); 
    } 

इस स्निपेट में, एन * 2 धागे शुरू कर रहे हैं कि सभी एक ही सूची पर काम ...

अलग व्यवहार Concurrnt संग्रह का उपयोग कर की जरूरत को समाप्त कर सकते हैं में निहित आप दो सूचियों के लिए के लिए।

+0

हालांकि इसका पूरी तरह से अलग व्यवहार है। –

+0

http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx –

+0

पूरी तरह से अलग व्यवहार। –

3

आपको पढ़ने की जरूरत है।नेट मेमोरी मॉडल, सामान्य रूप से अधिकारों के क्रम को प्रोसेसर पर परिभाषित नहीं किया जाता है, इसलिए साझा सूची में _list का एक नया मान लिखा जा सकता है, जबकि एक नई बनाई गई ऑब्जेक्ट का हिस्सा _list द्वारा इंगित किया गया है, फिर भी प्रोसेसर लिखने वाली कतार में है।

अधिकारों की पुनरावृत्ति के बारे में सोचने का दर्द है, इसलिए मैं Thread.MemoryBarrier() को सम्मिलित करने के लिए तम्बू करूंगा; इस तरह।

private void UpdateList() 
{  
    var newList = new List<string>(QueryList(...)); 
    Thread.MemoryBarrier(); 
    _list = newList; 
} 

अधिक जानकारी के

हालांकि मैं सबसे "सामान्य" प्रोसेसर पर लगता है कि आपके कोड लिखा रूप में काम करेंगे के लिए Threading in C# web page by Joseph Albahari देखें।

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