2013-11-22 6 views
40

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

मुझे पता है कि नीचे दिया गया कोड धागा सुरक्षित नहीं होना चाहिए, लेकिन मैं इसे भारी भार के तहत असफल नहीं कर सकता हूं और जटिलताओं को जटिल बनाने के लिए एक Google खोज दोनों तरीकों को कार्यान्वित करता है (बिना ताले के साथ ताले के साथ और बिना वे आवश्यक हैं

एक बहु थ्रेडेड वातावरण में मेमोरी कैश के ज्ञान वाले किसी व्यक्ति को मुझे निश्चित रूप से पता चलेगा कि मुझे उचित रूप से लॉक करने की आवश्यकता है या नहीं, ताकि एक कॉल को निकाला जा सके (जिसे शायद ही कभी बुलाया जाएगा लेकिन इसकी आवश्यकता होगी) पुनर्प्राप्ति/पुनर्निर्माण

public class MemoryCacheService : IMemoryCacheService 
{ 
    private const string PunctuationMapCacheKey = "punctuationMaps"; 
    private static readonly ObjectCache Cache; 
    private readonly IAdoNet _adoNet; 

    static MemoryCacheService() 
    { 
     Cache = MemoryCache.Default; 
    } 

    public MemoryCacheService(IAdoNet adoNet) 
    { 
     _adoNet = adoNet; 
    } 

    public void ClearPunctuationMaps() 
    { 
     Cache.Remove(PunctuationMapCacheKey); 
    } 

    public IEnumerable GetPunctuationMaps() 
    { 
     if (Cache.Contains(PunctuationMapCacheKey)) 
     { 
      return (IEnumerable) Cache.Get(PunctuationMapCacheKey); 
     } 

     var punctuationMaps = GetPunctuationMappings(); 

     if (punctuationMaps == null) 
     { 
      throw new ApplicationException("Unable to retrieve punctuation mappings from the database."); 
     } 

     if (punctuationMaps.Cast<IPunctuationMapDto>().Any(p => p.UntaggedValue == null || p.TaggedValue == null)) 
     { 
      throw new ApplicationException("Null values detected in Untagged or Tagged punctuation mappings."); 
     } 

     // Store data in the cache 
     var cacheItemPolicy = new CacheItemPolicy 
     { 
      AbsoluteExpiration = DateTime.Now.AddDays(1.0) 
     }; 

     Cache.AddOrGetExisting(PunctuationMapCacheKey, punctuationMaps, cacheItemPolicy); 

     return punctuationMaps; 
    } 

    //Go oldschool ADO.NET to break the dependency on the entity framework and need to inject the database handler to populate cache 
    private IEnumerable GetPunctuationMappings() 
    { 
     var table = _adoNet.ExecuteSelectCommand("SELECT [id], [TaggedValue],[UntaggedValue] FROM [dbo].[PunctuationMapper]", CommandType.Text); 
     if (table != null && table.Rows.Count != 0) 
     { 
      return AutoMapper.Mapper.DynamicMap<IDataReader, IEnumerable<PunctuationMapDto>>(table.CreateDataReader()); 
     } 

     return null; 
    } 
} 
+0

ObjectCache धागा सुरक्षित है, मुझे नहीं लगता कि आपकी कक्षा असफल हो सकती है। http://msdn.microsoft.com/en-us/library/system.runtime.caching.objectcache(v=vs.110).aspx आप एक ही समय में डेटाबेस पर जा रहे हैं लेकिन यह केवल अधिक cpu का उपयोग करेगा जरूरत से –

+0

ऑब्जेक्ट कैश थ्रेड सुरक्षित है, इसके कार्यान्वयन नहीं हो सकते हैं। इस प्रकार मेमोरी कैश सवाल। – Haney

उत्तर

34

डिफ़ॉल्ट एमएस-प्रो vided MemoryCache पूरी तरह से थ्रेड सुरक्षित है। MemoryCache से प्राप्त कोई भी कस्टम कार्यान्वयन थ्रेड सुरक्षित नहीं हो सकता है। यदि आप बॉक्स के बाहर MemoryCache का उपयोग कर रहे हैं, तो यह थ्रेड सुरक्षित है। मेरी खुला स्रोत वितरित कैशिंग समाधान के स्रोत कोड ब्राउज मैं इसे कैसे (MemCache.cs) का उपयोग देखने के लिए:

https://github.com/haneytron/dache/blob/master/Dache.CacheHost/Storage/MemCache.cs

+1

डेविड, बस पुष्टि करने के लिए, मेरे पास बहुत ही सरल उदाहरण वर्ग में है, कॉल करने के लिए। Remove() वास्तव में थ्रेड सुरक्षित है अगर कॉल करने की प्रक्रिया में एक अलग थ्रेड है()? मुझे लगता है कि मुझे केवल परावर्तक का उपयोग करना चाहिए और गहरी खुदाई करनी चाहिए लेकिन वहां बहुत सारी विवादित जानकारी है। –

+7

यह धागा सुरक्षित है, लेकिन दौड़ की स्थिति के लिए प्रवण है ... यदि आपका निकालना आपके निकालने से पहले होता है, तो डेटा प्राप्त करने पर वापस आ जाएगा। अगर निकालें पहले होता है, तो यह नहीं होगा। यह डेटाबेस पर गंदे पढ़ने की तरह है। – Haney

7

चेक बाहर इस लिंक: http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache(v=vs.110).aspx

जाओ पृष्ठ के बिल्कुल नीचे करने के लिए (या पाठ "थ्रेड सुरक्षा" के लिए खोजें)।

आप देखेंगे:

^थ्रेड सुरक्षा

इस प्रकार का धागा सुरक्षित है।

+4

मैंने व्यक्तिगत अनुभव के आधार पर कुछ समय पहले एमएसडीएन की "थ्रेड सेफ" की परिभाषा पर भरोसा करना बंद कर दिया था। यहां एक अच्छा पठन है: [लिंक] (http://stackoverflow.com/questions/3137931/msdn-what-is-thread-safety) –

+2

वह पोस्ट ऊपर दिए गए लिंक से थोड़ा अलग है। भेद बहुत महत्वपूर्ण है कि मैंने जो लिंक प्रदान किया है वह थ्रेड सुरक्षा की घोषणा के लिए कोई चेतावनी नहीं देता है।मेरे पास अभी तक कोई थ्रेडिंग समस्या नहीं होने के कारण बहुत मेमोरी (लाखों कैश हिट प्रति मिनट) में 'MemoryCache.Default' का उपयोग करके व्यक्तिगत अनुभव भी है। – EkoostikMartin

10

MemoryCache वास्तव में सुरक्षित थ्रेड जाता है के रूप में अन्य उत्तर निर्दिष्ट किया है, यह एक आम बहु सूत्रण मुद्दा है - एक ही समय में कैश करता है, तो 2 धागे Get से करने की कोशिश (या Contains जाँच), तो दोनों को याद करेंगे कैश और दोनों परिणाम उत्पन्न कर देंगे और दोनों परिणाम कैश में जोड़ देंगे।

अक्सर यह अवांछनीय है - दूसरे थ्रेड को पूरा करने के लिए पहले इंतजार करना चाहिए और परिणामों को दो बार उत्पन्न करने के बजाय इसके परिणाम का उपयोग करना चाहिए।

यह उन कारणों में से एक था जिन्हें मैंने LazyCache लिखा - मेमोरी कैश पर एक दोस्ताना रैपर जो इस तरह के मुद्दों को हल करता है। यह Nuget पर भी उपलब्ध है।

4

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

Reed Copsey कंसुरेंसी और ConcurrentDictionary<TKey, TValue> प्रकार के संबंध में अपने भयानक post में उद्धरण देने के लिए। जो निश्चित रूप से यहां लागू है।

यदि दो धागे इस [GetOrAdd] को एक साथ कहते हैं, तो टीवीएयू के दो उदाहरण आसानी से बनाया जा सकता है।

आप कल्पना कर सकते हैं कि TValue निर्माण करने के लिए महंगा है तो यह विशेष रूप से खराब होगा।

इसके आसपास अपना रास्ता काम करने के लिए, आप Lazy<T> बहुत आसानी से लाभ उठा सकते हैं, जो संयोग से निर्माण के लिए बहुत सस्ता है। ऐसा करने से यह सुनिश्चित होता है कि अगर हम एक बहुप्रचारित स्थिति में आते हैं, तो हम केवल Lazy<T> (जो सस्ता है) के कई उदाहरण बना रहे हैं।

GetOrAdd() (MemoryCache के मामले में GetOrCreate()) सभी धागे के लिए एक ही, एकवचन Lazy<T> वापस आ जाएगी, Lazy<T> की "अतिरिक्त" उदाहरणों बस फेंक दिया जाता है।

चूंकि Lazy<T>.Value कहलाता है, तब तक कुछ भी नहीं करता है, ऑब्जेक्ट का केवल एक उदाहरण कभी भी बनाया जाता है।

अब कुछ कोड के लिए! नीचे IMemoryCache के लिए एक विस्तार विधि है जो उपरोक्त लागू करती है। यह मनमाने ढंग से को int seconds विधि param के आधार पर सेट कर रहा है। लेकिन यह आपकी जरूरतों के आधार पर पूरी तरह से अनुकूलन योग्य है।

नोट इस विशिष्ट है क्षुधा

public static T GetOrAdd<T>(this IMemoryCache cache, string key, int seconds, Func<T> factory) 
{ 
    return cache.GetOrCreate<T>(key, entry => new Lazy<T>(() => 
    { 
     entry.SlidingExpiration = TimeSpan.FromSeconds(seconds); 

     return factory.Invoke(); 
    }).Value); 
} 

.netcore2.0 करने के लिए कॉल करने के लिए:

IMemoryCache cache; 
var result = cache.GetOrAdd("someKey", 60,() => new object()); 

सभी एसिंक्रोनस रूप से इस करने के लिए, मैं Stephen Toub's उत्कृष्ट AsyncLazy<T> कार्यान्वयन में पाया का उपयोग करना चाहिये एमएसडीएन पर उनके article। कौन सा वादा Task<T> साथ निर्मित आलसी प्रारंभकर्ता Lazy<T> को जोड़ती है:

public class AsyncLazy<T> : Lazy<Task<T>> 
{ 
    public AsyncLazy(Func<T> valueFactory) : 
     base(() => Task.Factory.StartNew(valueFactory)) 
    { } 
    public AsyncLazy(Func<Task<T>> taskFactory) : 
     base(() => Task.Factory.StartNew(() => taskFactory()).Unwrap()) 
    { } 
} 

अब GetOrAdd() की async संस्करण:

public static Task<T> GetOrAddAsync<T>(this IMemoryCache cache, string key, int seconds, Func<Task<T>> taskFactory) 
{ 
    return cache.GetOrCreateAsync<T>(key, async entry => await new AsyncLazy<T>(async() => 
    { 
     entry.SlidingExpiration = TimeSpan.FromSeconds(seconds); 

     return await taskFactory.Invoke(); 
    }).Value); 
} 

और अंत में, कॉल करने के लिए:

IMemoryCache cache; 
var result = await cache.GetOrAddAsync("someKey", 60, async() => new object()); 
संबंधित मुद्दे