2011-11-16 17 views
8

जोड़ने हम एक ASP.NET संदर्भ में जो एक आईआईएस 7 सर्वर पर चल रहा है में कोड की निम्न ब्लॉक में इस अपवाद होते हैं देख रहे हैं।अपवाद जब शब्दकोश प्रविष्टि

1) Exception Information 
********************************************* 
Exception Type: System.Exception 
Message: Exception Caught in Application_Error event 
Error in: InitializationStatus.aspx 
Error Message:An item with the same key has already been added. 
Stack Trace: at 
System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add) 
at CredentialsSession.GetXmlSerializer(Type serializerType) 

इस कोड है कि अपवाद में उत्पन्न किया जाता है:

[Serializable()] 
public class CredentialsSession 
{ 
    private static Dictionary<string, System.Xml.Serialization.XmlSerializer> localSerializers = new Dictionary<string, XmlSerializer>(); 

    private System.Xml.Serialization.XmlSerializer GetXmlSerializer(Type serializerType) 
    { 
     string sessionObjectName = serializerType.ToString() + ".Serializer"; 

     if (Monitor.TryEnter(this)) 
     { 
      try 
      { 
       if (!localSerializers.ContainsKey(sessionObjectName)) 
       { 
        localSerializers.Add(sessionObjectName, CreateSerializer(serializerType)); 
       } 
      } 
      finally 
      { 
       Monitor.Exit(this); 
      } 
     } 
     return localSerializers[sessionObjectName]; 
    } 

    private System.Xml.Serialization.XmlSerializer CreateSerializer(Type serializerType) 
    { 
     XmlAttributes xmlAttributes = GetXmlOverrides(); 

     XmlAttributeOverrides xmlOverrides = new XmlAttributeOverrides(); 
     xmlOverrides.Add(typeof(ElementBase), "Elements", xmlAttributes); 

     System.Xml.Serialization.XmlSerializer serializer = 
      new System.Xml.Serialization.XmlSerializer(serializerType, xmlOverrides); 

     return serializer; 
    } 
} 

Monitor.TryEnter एक साथ ब्लॉक में प्रवेश करने से एक से अधिक थ्रेड को रोकने होना चाहिए, और कोड के लिए शब्दकोश जाँच कर रहा है सत्यापित करें कि इसमें कुंजी शामिल नहीं है।

यह कैसे हो सकता है पर कोई विचार?

+0

+1, प्रश्न के लिए जो बताता है कि शब्दकोश में डुप्लिकेट कैसे ढूंढें। –

उत्तर

5

आपका कोड थ्रेड-सुरक्षित नहीं है।

  1. आप this पर ताला लगा रहे हैं, एक CredentialsSession उदाहरण है, लेकिन एक स्थिर शब्दकोश जो कई CredentialsSession उदाहरणों द्वारा साझा किया जा सकता तक पहुँचने। यह बताता है कि आपको त्रुटि क्यों मिल रही है - दो अलग-अलग CredentialsSession उदाहरण समवर्ती रूप से शब्दकोश को लिखने का प्रयास कर रहे हैं।

  2. भले ही आप इसे एक स्थिर क्षेत्र पर लॉक करने के लिए बदलते हैं जैसा कि @ एसएलएल के उत्तर में सुझाया गया है, आप थ्रेड-सुरक्षित नहीं हैं, क्योंकि आप शब्दकोश पढ़ने के दौरान लॉक नहीं कर रहे हैं। एकाधिक पाठकों और एक लेखक को कुशलतापूर्वक अनुमति देने के लिए आपको ReaderWriterLock या ReaderWriterLockSlim की आवश्यकता है।

    इसलिए आपको शायद थ्रेड-सुरक्षित शब्दकोश का उपयोग करना चाहिए। ConcurrentDictionary जैसा कि अन्य ने कहा है कि यदि आप .NET 4.0 का उपयोग कर रहे हैं। यदि नहीं, तो आपको अपना खुद का कार्यान्वयन करना चाहिए, या मौजूदा कार्यान्वयन जैसे http://devplanet.com/blogs/brianr/archive/2008/09/26/thread-safe-dictionary-in-net.aspx का उपयोग करना चाहिए।

आपकी टिप्पणियां सुझाव देती हैं कि आप एक ही प्रकार के लिए CreateSerializer पर कॉल करने से बचना चाहते हैं। मुझे नहीं पता क्यों, क्योंकि प्रदर्शन लाभ नगण्य होने की संभावना है, क्योंकि विवाद दुर्लभ होने की संभावना है और आवेदन के जीवनकाल के दौरान प्रत्येक प्रकार के लिए एक बार से अधिक नहीं हो सकता है।

लेकिन अगर आप वास्तव में इस चाहते हैं, तो आप इसे इस रूप में कर सकते हैं:

var value; 
if (!dictionary.TryGetValue(key, out value)) 
{ 
    lock(dictionary) 
    { 
     if(!dictionary.TryGetValue(key, out value)) 
     { 
      value = CreateSerializer(...); 
      dictionary[key] = value; 
     } 
    } 
} 

टिप्पणी से:

अगर मैं ConcurrentDictionary के साथ इस को लागू करने और बस फोन TryAdd (sessionObjectName, CreateSerializer (serializerType)) हर बार।

उत्तर हर बार TryAdd को कॉल नहीं करना है - पहले यह जांचें कि यह शब्दकोश में है या नहीं, तो यह जोड़ें कि यह नहीं है। the GetOrAdd overload का उपयोग करने के लिए एक बेहतर विकल्प हो सकता है जो Func तर्क लेता है।

+0

मैंने नहीं देखा था कि शब्दकोश स्थिर है! यह बहुत बताता है, धन्यवाद! मैं ConcurrentDictionary के साथ इसे फिर से कार्यान्वित कर रहा हूँ। – Avalanchis

+0

मान लें कि GetXmlSerializer को शब्दकोश से मूल्यों को पुनर्प्राप्त करने के लिए अक्सर बुलाया जाता है, अगर मैं इसे ConcurrentDictionary के साथ कार्यान्वित करता हूं और हर बार TryAdd (sessionObjectName, CreateSerializer (serializerType)) को कॉल करता हूं, तो CreateSerializer को हर बार GetXmlSerializer कहा जाता है। ऐसा लगता है कि मैं इससे बचना चाहता हूं। एक बार जब प्रत्येक कुंजी मान के लिए शब्दकोश पॉप्युलेट हो जाता है, तो CreateSerializer को और भी कॉल करने का कोई कारण नहीं है।सही बात? – Avalanchis

+2

+1 क्योंकि यह उत्तर पूरी तरह से मौजूदा समस्या का वर्णन करता है और विभिन्न मामलों के लिए इसे हल करने का सुझाव देता है – sll

5

localSerializers बल्कि कि this पर ताला लगा बाहर की कोशिश करो। बीटीडब्लू, आप मॉनीटर का स्पष्ट रूप से उपयोग क्यों कर रहे हैं? केवल एक ही कारण मैं देख रहा हूँ कि आप प्रयोग नहीं कर रहे हैं जो स्पष्ट रूप से lock timeout प्रदान करने के लिए है, तो बस का उपयोग lock() statement बजाय इस अंत में रूप में अच्छी तरह ट्राई/उत्पन्न होता है:

lock (localSerializers) 
{ 
    if (!localSerializers.ContainsKey(sessionObjectName))     
    {      
     localSerializers.Add(
      sessionObjectName, 
      CreateSerializer(serializerType));     
    } 
} 

संपादित करें: आप टैग में निर्दिष्ट नहीं किया है के बाद से कि आप .NET 4 का उपयोग कर रहे मैं ConcurrentDictionary<TKey, TValue>


Monitor.Enter() Method उपयोग करने का सुझाव होगा:

का प्रयोग करें एक सी # कोशिश ... अंत में ब्लॉक (का प्रयास करें ... विजुअल बेसिक में अंत में) है कि आप पर नजर रखने के रिलीज सुनिश्चित करने, या उपयोग करने के लिए सी # ताला बयान (विजुअल बेसिक में SyncLock बयान) है, जो दर्ज करें और में बाहर निकलें तरीकों लपेटता एक कोशिश ... अंत में ब्लॉक

+0

प्रतिक्रिया के लिए धन्यवाद! मूल रूप से मेरा कोड नहीं है, इसलिए मैं इसका उत्तर नहीं दे सकता कि यह मॉनीटर का स्पष्ट रूप से उपयोग क्यों कर रहा है। यह संसाधन को लॉक करने के लिए सही मायने रखता है जो इसके बजाए संरक्षित किया जा रहा है। मैं कोशिश करूँगा, धन्यवाद! – Avalanchis

+0

@Avalanchis: अपडेट किए गए उत्तर का हिस्सा संपादित करें, मैंने आपके प्रश्न के लिए .NET 4 टैग भी जोड़ा है, यह बहुत महत्वपूर्ण है – sll

+0

मुझे ConcurrentDictionary का उपयोग करने का विचार पसंद है, लेकिन मैं CreateSerializer को अनावश्यक कॉल के बारे में चिंतित हूं। ऐसा लगता है कि मुझे अभी भी ContainsKey को कॉल करने की आवश्यकता है यह देखने के लिए कि क्या TryAdd को कॉल करने से पहले कुंजी मौजूद है यदि मैं हर बार CreateSerializer को कॉल करना टालना चाहता हूं। सही बात? – Avalanchis

1

आप .NET फ्रेमवर्क 4 पर या बाद में कर रहे हैं, कि आप एक ConcurrentDictionary बजाय का उपयोग मेरा सुझाव है। TryAdd विधि आप परिदृश्य के इस प्रकार से सुरक्षित, कूड़े के ताले के साथ अपने कोड रहता है की आवश्यकता के बिना:

localSerializers.TryAdd(sessionObjectName, CreateSerializer(serializerType)) 

आप जब यह आवश्यक नहीं है लागू किया जा करने के लिए CreateSerializer के बारे में चिंतित हैं, तो आप के बजाय AddOrUpdate उपयोग करना चाहिए:

localSerializers.AddOrUpdate(
    sessionObjectName, 
    key => CreateSerialzer(serializerType), 
    (key, value) => value); 

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

+0

हम .NET 4 का उपयोग कर रहे हैं। क्या यह अभी भी यह देखने के लिए समझ में आता है कि क्या ConcurrentDictionary में पहले जोड़ा जाने वाला कुंजी है या नहीं? मुझे चिंतित है कि यदि कोशिश पहले से मौजूद है तो TryAdd अनावश्यक रूप से CreateSerializer को कॉल कर सकता है। – Avalanchis

+0

@Avalanchis: अद्यतन उत्तर देखें। –

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