我已經寫了一個通用的緩存類,用於返回內存中的對象,並且僅偶爾評估src(IQueryable或返回IQueryable的函數)。它在我的應用程序的幾個地方使用,通過實體框架獲取大量數據非常昂貴。緩存類和修改後的集合
它被稱爲
//class level
private static CachedData<Foo> fooCache= new CachedData<Foo>();
//method level
var results = fooCache.GetData("foos", fooRepo.Include("Bars"));
雖然它出現在測試工作確定,我看到了一些問題,一個繁忙的Web服務器上運行的「集合被修改;枚舉操作可能不會執行。」代碼中的錯誤消耗了結果。
這必須是因爲一個線程覆蓋了鎖內的結果對象,而另一個線程正在鎖外部使用它們。
我猜我唯一的解決辦法是將結果的副本返回給每個使用者而不是原來的,並且我不能允許複製在Fetch鎖內發生,但可以同時發生多個副本。
任何人都可以提出更好的方法,或幫助鎖定策略嗎?
public class CachedData<T> where T:class
{
private static Dictionary<string, IEnumerable<T>> DataCache { get; set; }
public static Dictionary<string, DateTime> Expire { get; set; }
public int TTL { get; set; }
private object lo = new object();
public CachedData()
{
TTL = 600;
Expire = new Dictionary<string, DateTime>();
DataCache = new Dictionary<string, IEnumerable<T>>();
}
public IEnumerable<T> GetData(string key, Func<IQueryable<T>> src)
{
var bc = brandKey(key);
if (!DataCache.ContainsKey(bc)) Fetch(bc, src);
if (DateTime.Now > Expire[bc]) Fetch(bc, src);
return DataCache[bc];
}
public IEnumerable<T> GetData(string key, IQueryable<T> src)
{
var bc = brandKey(key);
if ((!DataCache.ContainsKey(bc)) || (DateTime.Now > Expire[bc])) Fetch(bc, src);
return DataCache[bc];
}
private void Fetch(string key, IQueryable<T> src)
{
lock (lo)
{
if ((!DataCache.ContainsKey(key)) || (DateTime.Now > Expire[key])) ExecuteFetch(key, src);
}
}
private void Fetch(string key, Func<IQueryable<T>> src)
{
lock (lo)
{
if ((!DataCache.ContainsKey(key)) || (DateTime.Now > Expire[key])) ExecuteFetch(key, src());
}
}
private void ExecuteFetch(string key, IQueryable<T> src)
{
if (!DataCache.ContainsKey(key)) DataCache.Add(key, src.ToList());
else DataCache[key] = src.ToList();
if (!Expire.ContainsKey(key)) Expire.Add(key, DateTime.Now.AddSeconds(TTL));
else Expire[key] = DateTime.Now.AddSeconds(TTL);
}
private string brandKey(string key, int? brandid = null)
{
return string.Format("{0}/{1}", brandid ?? Config.BrandID, key);
}
}
您的緩存完全不是線程安全的,不應使用。 – SLaks
是@SLaks我知道:-(! – Andiih