Ich gehe davon aus, dass dieser Code Probleme mit der Parallelität hat:
const string CacheKey = "CacheKey";
static string GetCachedData()
{
string expensiveString =null;
if (MemoryCache.Default.Contains(CacheKey))
{
expensiveString = MemoryCache.Default[CacheKey] as string;
}
else
{
CacheItemPolicy cip = new CacheItemPolicy()
{
AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddMinutes(20))
};
expensiveString = SomeHeavyAndExpensiveCalculation();
MemoryCache.Default.Set(CacheKey, expensiveString, cip);
}
return expensiveString;
}
Der Grund für das Problem der Parallelität besteht darin, dass mehrere Threads einen Nullschlüssel erhalten und dann versuchen können, Daten in den Cache einzufügen.
Was wäre der kürzeste und sauberste Weg, um diesen Code gleichzeitig zu prüfen? Ich folge gerne einem guten Muster in meinem Cache-Code. Ein Link zu einem Online-Artikel wäre eine große Hilfe.
AKTUALISIEREN:
Ich habe diesen Code basierend auf der Antwort von @Scott Chamberlain entwickelt. Kann jemand ein Leistungs- oder Parallelitätsproblem damit finden? Wenn dies funktioniert, würden viele Codezeilen und Fehler eingespart.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.Caching;
namespace CachePoc
{
class Program
{
static object everoneUseThisLockObject4CacheXYZ = new object();
const string CacheXYZ = "CacheXYZ";
static object everoneUseThisLockObject4CacheABC = new object();
const string CacheABC = "CacheABC";
static void Main(string[] args)
{
string xyzData = MemoryCacheHelper.GetCachedData<string>(CacheXYZ, everoneUseThisLockObject4CacheXYZ, 20, SomeHeavyAndExpensiveXYZCalculation);
string abcData = MemoryCacheHelper.GetCachedData<string>(CacheABC, everoneUseThisLockObject4CacheXYZ, 20, SomeHeavyAndExpensiveXYZCalculation);
}
private static string SomeHeavyAndExpensiveXYZCalculation() {return "Expensive";}
private static string SomeHeavyAndExpensiveABCCalculation() {return "Expensive";}
public static class MemoryCacheHelper
{
public static T GetCachedData<T>(string cacheKey, object cacheLock, int cacheTimePolicyMinutes, Func<T> GetData)
where T : class
{
//Returns null if the string does not exist, prevents a race condition where the cache invalidates between the contains check and the retreival.
T cachedData = MemoryCache.Default.Get(cacheKey, null) as T;
if (cachedData != null)
{
return cachedData;
}
lock (cacheLock)
{
//Check to see if anyone wrote to the cache while we where waiting our turn to write the new value.
cachedData = MemoryCache.Default.Get(cacheKey, null) as T;
if (cachedData != null)
{
return cachedData;
}
//The value still did not exist so we now write it in to the cache.
CacheItemPolicy cip = new CacheItemPolicy()
{
AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddMinutes(cacheTimePolicyMinutes))
};
cachedData = GetData();
MemoryCache.Default.Set(cacheKey, cachedData, cip);
return cachedData;
}
}
}
}
}
Dictionary<string, object>
Schlüssel durchgeführt werden, bei dem der Schlüssel derselbe ist, den Sie in Ihrem verwenden, MemoryCache
und das Objekt im Wörterbuch nur eine Grundvoraussetzung ist, die Object
Sie festlegen. Trotzdem würde ich Ihnen empfehlen, Jon Hannas Antwort durchzulesen. Ohne eine ordnungsgemäße Profilierung verlangsamen Sie Ihr Programm möglicherweise mehr durch Sperren als durch Anlassen von zwei Laufinstanzen SomeHeavyAndExpensiveCalculation()
und lassen ein Ergebnis wegwerfen.
ReaderWriterLockSlim
?