2011-07-31 17 views
5

कुछ डेटाबेस कोड एक बग इस सवाल से संबंधित नहीं की तलाश में से गुजर रही है, मैंने देखा है कि कुछ स्थानों में List<T> अनुपयुक्त इस्तेमाल किया जा रहा था। विशेष रूप से:धागा, एक लेखक के साथ सूची <T> की सुरक्षा नहीं ूगणकों

  1. कई सूत्र समवर्ती पाठकों के रूप में List तक पहुँचने, लेकिन अनुक्रमित enumerators के बजाय list में का उपयोग कर रहे थे।
  2. list के लिए एक एकल लेखक नहीं था।
  3. शून्य तुल्यकालन, पाठकों और लेखकों एक ही समय में list तक पहुँचने गया था, लेकिन कोड संरचना की वजह से पिछले तत्व विधि है कि मार डाला Add() लौटे जब तक कभी नहीं पहुँचा जा जाएगा।
  4. कोई तत्व कभी list से हटा दिया गया।

सी # प्रलेखन रूप से, यह सुरक्षित नहीं थ्रेड किया जाना चाहिए। फिर भी यह कभी विफल नहीं हुआ है। मैं List के विशिष्ट कार्यान्वयन की वजह से, सोच रहा हूँ (मैं आंतरिक रूप से यह मानते हुए कि कर रहा हूँ यह कि फिर से allocs जब यह स्थान से बाहर चलाता है सरणी एक है), यह 1-लेखक 0-प्रगणक एन पाठक एड-केवल परिदृश्य गलती से धागा सुरक्षित , या वहाँ कुछ संभावना नहीं परिदृश्य में जहाँ इस वर्तमान .NET4 कार्यान्वयन में उड़ा सकता है?

संपादित करें: महत्वपूर्ण विस्तार मैं उत्तर के कुछ पढ़ने बाहर छोड़ दिया। पाठकों List और के रूप में केवल पढ़ने के लिए उसकी सामग्री का इलाज।

उत्तर

1

इसकी वजह कभी विफल नहीं हुई है या आपका एप्लिकेशन क्रैश नहीं होता है इसका मतलब यह नहीं है कि यह परिदृश्य थ्रेड सुरक्षित है। उदाहरण के लिए मान लीजिए कि लेखक थ्रेड सूची के भीतर एक फ़ील्ड अपडेट करता है, मान लें कि long फ़ील्ड था, साथ ही पाठक थ्रेड उस फ़ील्ड को पढ़ रहा था। मूल्य दो क्षेत्रों के पुराने और एक नया हो सकता है! कि हो सकता है क्योंकि पाठक धागा स्मृति से मूल्य पढ़ना शुरू लेकिन इससे पहले कि यह लेखक धागा पढ़ चुकी है बस इसे अपडेट किया गया।

संपादित करें: जाहिर है अगर हम मान लें कि है कि पाठक धागे कुछ भी अपडेट किए बिना सभी डेटा पढ़ा जाएगा, मुझे यकीन है कि वे सरणियों उन्हें स्वयं लेकिन के मूल्यों में परिवर्तन नहीं करता हूँ, लेकिन वे बदल सकता है एक मूल्य या फ़ील्ड जो वे पढ़ते हैं उसके भीतर। उदाहरण के लिए:

for (int index =0 ; index < list.Count; index++) 
{ 
    MyClass myClass = list[index];//ok we are just reading the value from list 
    myClass.SomeInteger++;//boom the same variable will be updated from another threads... 
} 

इस उदाहरण में ही सूची के बजाय साझा चर सूची उजागर की सुरक्षित धागा बारे में बात नहीं।

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

+1

सूची की थ्रेड सुरक्षा में सूची में मौजूद ऑब्जेक्ट के फ़ील्ड की थ्रेड-सुरक्षा के साथ * कुछ भी * नहीं है। भले ही आप वास्तविक सूची थ्रेड-सुरक्षित बनाते हैं, जिस समस्या का आप वर्णन कर रहे हैं वह अभी भी मौजूद होगा। –

+0

@ ब्रॉइस हाँ आप सही हैं, उन्हें मल्टीथ्रेड से अपडेट की जा सकने वाली प्रत्येक चीज़ के लिए लॉक करना चाहिए। –

+0

@Chuu कोई प्रतिक्रिया? –

2

यह उड़ सकता है और उड़ जाएगा। यह अभी तक अभी तक नहीं है। बाँझ सूचकांक आमतौर पर पहली चीज है जो जाता है। यह तब उड़ाएगा जब आप इसे नहीं चाहते हैं।आप इस समय शायद भाग्यशाली हैं।

जैसा कि आप .NET 4.0 का उपयोग कर रहे हैं, मैं सलाह देता हूं कि सूची को सिस्टम से एक उचित संग्रह में बदलना होगा। चयन। कॉन्कुरेंट जो थ्रेड सुरक्षित होने की गारंटी है। मैं भी ConcurrentDictionary को सरणी सूचकांक और स्विच का उपयोग करने से बचने चाहते हैं, तो आप कुछ देखने की जरूरत है:

http://msdn.microsoft.com/en-us/library/dd287108.aspx

0

यह है, तो इस प्रकार यदि वास्तुकला 32 बिट, एक क्षेत्र से बड़े 32 बिट लेखन, ऐसा है कि लंबे और डबल के रूप में, एक थ्रेड सुरक्षित संचालन नहीं है; System.Double के लिए दस्तावेज़ देखें:

इस प्रकार का एक उदाहरण नियत सभी हार्डवेयर प्लेटफार्मों पर सुरक्षित थ्रेड नहीं है, क्योंकि है कि उदाहरण के द्विआधारी प्रतिनिधित्व एकल परमाणु ऑपरेशन में आवंटित करने के लिए बहुत बड़ा हो सकता है।

यदि सूची आकार में तय की गई है, हालांकि, यह स्थिति केवल तभी मायने रखती है जब सूची 32 बिट्स से अधिक मूल्य प्रकारों को संग्रहीत कर रही हो। यदि सूची केवल संदर्भ प्रकार धारण कर रही है, तो किसी भी थ्रेड सुरक्षा समस्या संदर्भ प्रकार से स्वयं ही होती है, न कि उनके संग्रहण और सूची से पुनर्प्राप्ति से। उदाहरण के लिए, अपरिवर्तनीय संदर्भ प्रकार म्यूटेबल संदर्भ प्रकारों की तुलना में थ्रेड सुरक्षा समस्याओं का कारण बनने की संभावना कम हैं।

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

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

+0

सूची 32 बिट सिस्टम पर 32 बिट प्रकारों के लिए थ्रेड सुरक्षित नहीं है या तो सूची में कोई आइटम जोड़ना एक से अधिक मान लिखता है। कम से कम इसे नया आइटम लिखना होगा और सूची आकार अपडेट करना होगा - एक बार जब आप लॉक के बिना एक से अधिक ऑपरेशन कर रहे हों तो वहां कोई थ्रेड सुरक्षा नहीं है। यह उल्लेख नहीं करना चाहिए कि सूची कब फिर से आकार देगी। – shf301

+0

मैं देखता हूं, नोट के लिए धन्यवाद। मैं जवाब अपडेट कर दूंगा। –

0

थ्रेड सुरक्षा केवल तभी महत्वपूर्ण होती है जब डेटा एक बार में एक से अधिक बार संशोधित होता है। पाठकों की संख्या कोई फर्क नहीं पड़ता। यहां तक ​​कि जब कोई पढ़ता है तो कोई लिख रहा है, पाठक को पुराना डेटा या नया मिलता है, यह अभी भी काम करता है। तथ्य यह है कि तत्वों को केवल() रिटर्न के बाद एक्सेस किया जा सकता है, तत्व के हिस्सों को अलग से पढ़ा जा सकता है। यदि आप Insert() विधि पाठकों का उपयोग शुरू करना चाहते हैं तो गलत डेटा प्राप्त हो सकता है।

+0

मैं सहमत हूं, विशेष रूप से पाठकों के केवल पढ़ने के उपयोग के संबंध में ओपी के संपादन के बाद। –

+0

-1। यह सिर्फ गलत है: * "जब कोई पढ़ता है, तब भी कोई लिखता है, पाठक को या तो पुराना डेटा या नया मिलता है, यह अभी भी काम करता है।" * चाहे वह काम करता है या नहीं, उस विशेष डेटा संरचना का कार्यान्वयन विवरण है, और जब तक इसकी गारंटी है तो आप इस पर भरोसा नहीं कर सकते हैं। ['सूची 'दस्तावेज़ीकरण] (http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx) कहता है कि यह थ्रेडसेफ तरीके से एकाधिक पाठकों-नो-राइटर्स का समर्थन कर सकता है। ओपी कई पाठकों-एकल-लेखक के साथ अब तक भाग्यशाली रहा है लेकिन इस बात की कोई गारंटी नहीं है कि यह मामला जारी रहेगा। – LukeH

+0

मुझे लगता है कि समस्या को संबोधित किया गया है क्योंकि वह केवल एक ही लिखने का उपयोग करता है और नया तत्व केवल एड रिटर्न के बाद सुलभ हो जाता है। याद रखें, वह कुछ कस्टम सूची कार्यान्वयन का उपयोग कर रहा है, न कि .NET Framework से सूची संस्करण। – MrFox

0

कुछ पदों और टिप्पणियों के लिए सबसे पहले, जब दस्तावेज़ीकरण विश्वसनीय था?

दूसरा, यह उत्तर ओपी के विनिर्देशों की तुलना में सामान्य प्रश्न के लिए अधिक है।

मैं सिद्धांत में MrFox से सहमत क्योंकि यह सब दो सवाल करने पर निर्भर करता:

  1. सूची वर्ग एक फ्लैट सरणी के रूप में कार्यान्वित किया जाता है है?

यदि हाँ, तो:

  1. एक लिखने अनुदेश एक लिखने>

के बीच में रोका जाता जा सकता है मेरा मानना ​​है कि यह स्थिति नहीं है - पूर्ण लिखने क्या होगा कुछ भी डीडब्ल्यूओआर या जो कुछ भी पढ़ सकता है उससे पहले।दूसरे शब्दों में, यह कभी नहीं होगा कि मैं एक DWORD के चार बाइट्स लिखता हूं और फिर आप नए मान के 1/2 और पुराने के 1/2 को पढ़ते हैं।

तो, यदि आप किसी सूचक को ऑफ़सेट प्रदान करके सरणी को अनुक्रमणित कर रहे हैं, तो आप थ्रेड-लॉकिंग के बिना सुरक्षित रूप से पढ़ सकते हैं। यदि सूची केवल साधारण सूचक गणित से अधिक कर रही है, तो यह थ्रेड सुरक्षित नहीं है।

यदि सूची एक फ्लैट सरणी का उपयोग नहीं कर रही थी, तो मुझे लगता है कि आप इसे अब तक क्रैश कर चुके होंगे।

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

सबसे खराब मामला है, इस तरह के अगर आप सूची के माध्यम से पुनरावृति करने की आवश्यकता के रूप में, तो सबसे अच्छा होगा है:

  1. सूची
  2. ताला एक सरणी एक ही आकार
  3. उपयोग CopyTo create() सूची को प्रतिलिपि बनाने के लिए
  4. सूची
  5. अनलॉक करें, फिर सूची के बजाय सरणी के माध्यम से पुनरावृत्ति करें।
में (जो भी आप .net कहते हैं) सी ++

:

List<Object^>^ objects = gcnew List<Object^>^(); 
    // in some reader thread: 
    Monitor::Enter(objects); 
    array<Object^>^ objs = gcnew array<Object^>(objects->Count); 
    objects->CopyTo(objs); 
    Monitor::Exit(objects); 
    // use objs array 

यहां तक ​​कि स्मृति आवंटन के साथ, इस सूची पर ताला लगा है और इसे अनलॉक करने से पहले पूरी बात के माध्यम से पुनरावृत्ति की तुलना में तेजी हो जाएगा।

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

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