2012-02-16 21 views
5

के साथ मुझे अपने आवेदन में डेडलॉक्स के साथ एक दिलचस्प समस्या है। एक इन-मेमोरी डेटा स्टोर है जो रीडर्स और लिखने को सिंक्रनाइज़ करने के लिए रीडरवाइटर लॉकस्लिम का उपयोग करता है। पढ़ने के तरीकों में से एक पैरालेल का उपयोग करता है। फ़िल्टर के सेट दिए गए स्टोर को खोजने के लिए। यह संभव है कि फ़िल्टरों में से एक को एक ही स्टोर के निरंतर समय के पढ़ने की आवश्यकता हो। यहां एक परिदृश्य है जो डेडलॉक का उत्पादन कर रहा है:समानांतर में डेडलॉक। ReaderWriterLockSlim

अद्यतन: नीचे उदाहरण कोड। वास्तविक विधि के साथ अद्यतन कदम कॉल
को देखते हुए सिंगलटन उदाहरण ConcreteStoreThatExtendsGenericStore

  1. की store Thread1 दुकान पर एक रीड ताला हो जाता है - store.Update() - एक लिखने लॉक के साथ दुकान को अद्यतन करने के store.Search(someCriteria)
  2. Thread2 प्रयास - , के पीछे ब्लॉक थ्रेड 1
  3. थ्रेड 1 समानांतर निष्पादित करता है दुकान के खिलाफ orEach फिल्टर
  4. Thread3 एक निरंतर समय दुकान के पढ़ने के प्रयास करता है (द्वारा Thread1 के Parallel.ForEach पैदा की) का एक सेट चलाने के लिए। यह एक रीड लॉक प्राप्त करने का प्रयास करता है लेकिन थ्रेड 2 के लिखने के लॉक के पीछे अवरुद्ध है।
  5. थ्रेड 1 समाप्त नहीं हो सकता क्योंकि यह थ्रेड 3 में शामिल नहीं हो सकता है। थ्रेड 2 समाप्त नहीं हो सकता क्योंकि यह थ्रेड 1 के पीछे अवरुद्ध है।

आदर्श रूप में मैं क्या करना चाहते हैं नहीं यदि वर्तमान धागा के एक पूर्वज धागा पहले से ही एक ही ताला है पढ़ने ताला पाने की कोशिश है। क्या इसे करने का कोई तरीका है? या क्या कोई और बेहतर दृष्टिकोण है?

public abstract class GenericStore<TKey, TValue> 
{ 
    private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); 
    private List<IFilter> _filters; //contains instance of ExampleOffendingFilter 

    protected Dictionary<TKey, TValue> Store { get; private set; } 

    public void Update() 
    { 
     _lock.EnterWriterLock(); 
     //update the store 
     _lock.ExitWriteLock(); 
    } 

    public TValue GetByKey(TKey key) 
    { 
     TValue value; 
     //TODO don't enter read lock if current thread 
     //was started by a thread holding this lock 
     _lock.EnterReadLock(); 
     value = Store[key]; 
     _lock.ExitReadLock(); 
     return value; 
    } 

    public List<TValue> Search(Criteria criteria) 
    { 
     List<TValue> matches = new List<TValue>(); 
     //TODO don't enter read lock if current thread 
     //was started by a thread holding this lock 
     _lock.EnterReadLock(); 
     Parallel.ForEach(Store.Values, item => 
     { 
      bool isMatch = true; 
      foreach(IFilter filter in _filters) 
      { 
       if (!filter.Check(criteria, item)) 
       { 
        isMatch = false; 
        break; 
       } 
      } 
      if (isMatch) 
      { 
       lock(matches) 
       { 
        matches.Add(item); 
       } 
      } 
     }); 
     _lock.ExitReadLock(); 
     return matches; 
    } 
} 

public class ExampleOffendingFilter : IFilter 
{ 
    private ConcreteStoreThatExtendsGenericStore _sameStore; 

    public bool Check(Criteria criteria, ConcreteValueType item) 
    { 
     _sameStore.GetByKey(item.SomeRelatedProperty); 
     return trueOrFalse; 
    } 
} 
+0

क्या इन-मेमोरी स्टोर एक कस्टम प्रकार है या यह एक सूची है जो कि किसी अन्य श्रेणी के भीतर एक फ़ील्ड है? –

+0

जो आपने फ़ोरैच में चल रहे हैं उसमें लॉक किया है, अन्यथा इसे पढ़ने के बाद समानांतर करना समय की बर्बादी है। समानांतर में कभी भी दौड़ने के लिए नहीं जा रहे हैं क्योंकि वे सभी एक ही संसाधन पर बाधित हैं। –

+0

@TrevorPilley: स्टोर एक शब्दकोश है , समांतर के साथ। मूल्यों के लिए – eakins05

उत्तर

1

यह स्पष्ट नहीं है कि वास्तव में आपके पास कितनी समेकन, स्मृति और प्रदर्शन आवश्यकताओं की आवश्यकता है, यहां कुछ विकल्प हैं।

यदि आप .NET 4.0 का उपयोग कर रहे हैं, तो आप को ConcurrentDictionary के साथ प्रतिस्थापित कर सकते हैं और अपना ReaderWriterLockSlim हटा सकते हैं। ध्यान रखें कि ऐसा करने से आपके लॉकिंग स्कोप को कम कर दिया जाएगा और आपके विधि अर्थशास्त्र को बदल दिया जाएगा, जबकि आप गणना करते समय सामग्री में परिवर्तन की अनुमति दे सकते हैं (दूसरी चीजों के साथ), लेकिन दूसरी ओर जो आपको थ्रेडसेफ एन्यूमेरेटर देगा जो ब्लॉक नहीं करेगा पढ़ता है या लिखता है। आपको यह निर्धारित करना होगा कि क्या यह आपकी स्थिति के लिए एक स्वीकार्य परिवर्तन है।

यदि आपको वास्तव में पूरे संग्रह को इस तरह से लॉक करने की आवश्यकता है, तो आप एक रिकर्सिव लॉक पॉलिसी (new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion)) का समर्थन करने में सक्षम हो सकते हैं यदि आप सभी कार्यों को एक ही थ्रेड पर रख सकते हैं। समानांतर में अपनी खोज कर रहे हैं?

वैकल्पिक रूप से, आप अपने वर्तमान मूल्यों (उस ऑपरेशन के चारों ओर लॉकिंग) का एक स्नैपशॉट प्राप्त करना चाहते हैं और फिर स्नैपशॉट के खिलाफ अपनी खोज निष्पादित करना चाहते हैं। यह नवीनतम डेटा रखने की गारंटी नहीं दी जाएगी और आपको रूपांतरण पर थोड़ा समय बिताना होगा, लेकिन शायद यह आपकी स्थिति के लिए एक स्वीकार्य व्यापार है।

+0

मुझे ConcurrentDictionary दृष्टिकोण पसंद है, लेकिन दुर्भाग्यवश हम केवल स्टोरेज (इंडेक्स, इत्यादि) से अधिक बनाए रखते हैं, जो लिखने के लॉक के पीछे होना चाहिए। समांतरता को हटाने का एक विकल्प हो सकता है जिसे हमें पीओसी करना होगा। स्नैपशॉट विचार कुछ ऐसा है जिसे हमने पहले खिलवाड़ किया है, लेकिन इस तरह की एक आदर्श बदलाव के साथ, हम इस विरासत को घर के स्थानांतरित करने के लिए एक कैशिंग समाधान खरीदने पर विचार कर रहे हैं, इसलिए हम शायद उस मार्ग पर जायेंगे। अंतर्दृष्टि के लिए धन्यवाद! – eakins05

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