2012-03-28 10 views
9

मेरे पास कई मामले हैं जहां मैं मूल्यों के कैशिंग के लिए ConcurrentDictionary<TKey, TValue> का उपयोग करता हूं, लेकिन अक्सर यह तय करने के लिए कि मुझे ConcurrentDictionary<TKey, TValue>.GetOrAdd(TKey, Func<TKey, TValue>) का उपयोग करके कैश में जोड़ना है या नहीं, यह मानने के लिए मूल्य की सत्यापन करने की आवश्यकता है।मैं मूल्य जोड़ने के लिए 'ConcurrentDictionary.GetOrAdd` कैसे कह सकता हूं?

आमतौर पर की तर्ज पर

:

private readonly ConcurrentDictionary<Type, ISomeObject> someObjectCache = 
    new ConcurrentDictionary<Type, ISomeObject>(); 
public ISomeObject CreateSomeObject(Type someType) 
{ 
    return someObjectCache.GetOrAdd(someType, type => 
    { 
     if(!Attribute.IsDefined(someType, typeof(SomeAttribute)) 
      // Do something here to avoid the instance from being added to 
      // `someObjectCache` 

     ISomeObject someObject; 
     // Typical factory functionality goes here 
     return someObject; 
    }); 
} 

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

अन्य LINQ जैसे विधियों के साथ अपने अनुभव के आधार पर, null लौटने के परिणामस्वरूप मूल्य को बिना जांच किए जोड़ा जा सकता है (और GetOrAdd के लिए आईएल पढ़ना ऐसा लगता है कि यह एक ही समस्या का परिणाम देगा), इसलिए मैं ऐसा मत सोचो कि काम करेगा।

क्या कोई तरीका है कि मैं GetOrAdd का उपयोग करके एड को रद्द करने के अपवादों का उपयोग करने से बच सकता हूं?

उत्तर

10

जो मैंने पढ़ा है, उससे no guarantee that the Add factory method will only be called a single time amongst all callers to Get for the same key है।

उस पृष्ठ से संबंधित भाग नीचे स्थित है, यहाँ उद्धृत किया:

इसके अलावा, हालांकि ConcurrentDictionary (TKey से TValue) के सभी तरीकों धागा सुरक्षित हैं, नहीं सभी तरीकों, परमाणु कर रहे हैं विशेष रूप से GetOrAdd और AddOrUpdate। उपयोगकर्ता विधियों को इन विधियों में पारित किया गया है शब्दकोष के आंतरिक लॉक के बाहर बुलाया गया है। (यह लिए किया जाता है अवरुद्ध सभी धागे से अज्ञात कोड को रोकने के।) इसलिए यह संभव है की घटनाओं के इस दृश्य के लिए होने के लिए:

1) threadA कॉल GetOrAdd, कोई आइटम पाता है और एक नया आइटम से जोड़ें बनाता है मूल्य फैक्ट्री प्रतिनिधि का आह्वान किया।

2) threadB GetOrAdd समवर्ती कहता है, इसकी valueFactory प्रतिनिधि लागू है और यह threadA से पहले आंतरिक ताला पर आता है, और इसलिए इसकी नई कुंजी-मान पेयर शब्दकोश में जोड़ा जाता है।

3) threadA के उपयोगकर्ता प्रतिनिधि पूर्ण करता है और धागा ताला पर आता है, लेकिन अब देखता है कि आइटम पहले से मौजूद

4) threadA एक "जाओ" करता है, और डेटा है कि पहले था जोड़ा रिटर्न थ्रेड बी द्वारा।

इसलिए, यह गारंटी नहीं है कि द्वारा लौटाया गया डेटा GetOrAdd वही डेटा है जो थ्रेड के वैल्यू फैक्टरी द्वारा बनाया गया था। घटनाओं का एक समान अनुक्रम तब हो सकता है जब AddOrUpdate कहा जाता है।

जिस तरह से मैंने इसे पढ़ा है, भले ही आप अपने जोड़ें प्रतिनिधि में कुछ लॉकिंग कॉल कर रहे हों, आपको गारंटी नहीं है कि आपके ऐड से लौटाया गया मूल्य वह है जिसका उपयोग वास्तव में किया जाएगा।

private ConcurrentDictionary<Type, ISomeObject> someObjectCache = 
    new ConcurrentDictionary<Type, ISomeObject>(); 
public ISomeObject CreateSomeObject(Type someType) 
{ 

    ISomeObject someObject; 
    if (someObjectCache.TryGet(someType, out someObject)) 
    { 
     return someObject; 
    } 

    if (Attribute.IsDefined(someType, typeof(SomeAttribute)) 
    { 
     // init someObject here 
     someObject = new SomeObject(); 

     return someObjectCache.GetOrAdd(someType, someObject); // If another thread got through here first, we'll return their object here. 
    } 

    // fallback functionality goes here if it doesn't have your attribute. 
} 

हाँ, यह कुछ संभावित में परिणाम होगा नई वस्तुओं के लिए संभावित रूप से कई बार बनाया जाना:

तो, आप शायद निम्नलिखित पैटर्न इस्तेमाल कर सकते हैं किसी भी आगे ताला जोड़ने की जरूरत नहीं होनी चाहिए, और बदले , लेकिन कॉलर्स को एक ही परिणाम मिल जाएगा, भले ही एकाधिक कहा जाता है। GetOrAdd के समान ही है।

+0

यह (और _I have_) होगा, लेकिन एक बार जब आप अव्यवस्थित हो जाएंगे कि सभी आवश्यक थ्रेड लॉकिंग तंत्र के साथ, यह बदसूरत हो जाता है (नहीं कि मुझे लॉकिंग की आवश्यकता नहीं है ... बस एक ही हद तक नहीं)। मैं एक क्लीनर समाधान की तलाश में हूं, ऐसा नहीं है जिसके लिए प्रभावी रूप से 'ConcurrentDictionary' के अपने संस्करण को रोल करने की आवश्यकता है। –

+0

मेरा उत्तर अपडेट किया गया - GetOrAdd केवल आंतरिक सरणी बिट्स से निपटने के लिए, भाग को लॉक नहीं करता है। इसलिए, जैसा कि मैंने इसे देखा है, आपको यहां किसी भी ताले की आवश्यकता नहीं है यदि आपका इरादा केवल सत्यापन जोड़ना है। –

+0

हां, मैंने दस्तावेज़ों के उस हिस्से को पढ़ लिया है, लेकिन यह अभी भी वह नहीं है जिसे मैं ढूंढ रहा हूं। अगर मैं इतना कोड लिखना चाहता था तो मैं खुद को लॉक कर दूंगा। –

3

कंप्यूटर विज्ञान में सभी समस्याओं अविवेक के एक अन्य स्तर से हल किया जा सकता

// the dictionary now stores functions 
private readonly ConcurrentDictionary<Type, Func<ISomeObject>> someObjectCache = 
    new ConcurrentDictionary<Type, Func<ISomeObject>>(); 

public ISomeObject CreateSomeObject(Type someType) { 
    return someObjectCache.GetOrAdd(someType, _ => { 
    if(ShouldCache(someType)) { 
     // caching should be used 
     // return a function that returns a cached instance 
     var someObject = Create(someType); 
     return() => someObject; 
    } 
    else { 
     // no caching should be used 
     // return a function that always creates a new instance 
     return() => Create(someType); 
    } 
    })(); // call the returned function 
} 

private bool ShouldCache(Type someType) { 
    return Attribute.IsDefined(someType, typeof(SomeAttribute)); 
} 

private ISomeObject Create(Type someType) { 
    // typical factory functionality ... 
} 

अब डिक्शनरी में संग्रहीत किए मूल्य एक समारोह है; जब आप कैशिंग नहीं करना चाहते हैं, तो फ़ंक्शन हमेशा एक नया उदाहरण बनाता है; जब आप कैशिंग करना चाहते हैं, तो फ़ंक्शन कैश्ड उदाहरण देता है।

+0

क्या आप वाकई काम करेंगे? आपने जो कुछ किया है, वह चेक को फ़ंक्शन में ले जाना है और वैसे भी प्रकार का एक उदाहरण वापस करना है। शायद कुछ और स्पष्टीकरण की आवश्यकता है। –

+0

निश्चित रूप से, मैं कुछ और जोड़ दूंगा .... –

+0

चेक को एक अलग फ़ंक्शन में ले जाने के लिए सवाल के दिल से कोई लेना देना नहीं है, यह केवल कुछ सामान्य रिफैक्टरिंग था .... –

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

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