我通过System.Web.Caching.Cache-Class在ASP.NET网站中缓存数据,因为检索数据非常昂贵,并且当我们的内容人员在后端更改数据时,它只会偶尔更改一次
所以我在Application_Start中创建数据并将其存储在Cache中,过期时间为1天。
访问数据时(在网站的许多页面上发生),我现在在静态CachedData类中有类似的东西:
public static List<Kategorie> GetKategorieTitelListe(Cache appCache)
{
// get Data out of Cache
List<Kategorie> katList = appCache[CachedData.NaviDataKey] as List<Kategorie>;
// Cache expired, retrieve and store again
if (katList == null)
{
katList = DataTools.BuildKategorienTitelListe();
appCache.Insert(CachedData.NaviDataKey, katList, null, DateTime.Now.AddDays(1d), Cache.NoSlidingExpiration);
}
return katList;
}
我在这段代码中看到的问题是它不是线程安全的。 如果两个用户同时打开其中两个页面并且缓存刚用完,那么在多次检索数据时存在风险。
但是如果我锁定方法体,我将遇到性能问题,因为一次只有一个用户可以获取数据列表。
有一种简单的方法可以防止这种情况发生吗?对于这样的案例,最佳做法是什么?
答案 0 :(得分:4)
你是对的,你的代码不是线程安全的。
// this must be class level variable!!!
private static readonly object locker = new object();
public static List<Kategorie> GetKategorieTitelListe(Cache appCache)
{
// get Data out of Cache
List<Kategorie> katList = appCache[CachedData.NaviDataKey] as List<Kategorie>;
// Cache expired, retrieve and store again
if (katList == null)
{
lock (locker)
{
katList = appCache[CachedData.NaviDataKey] as List<Kategorie>;
if (katlist == null) // make sure that waiting thread is not executing second time
{
katList = DataTools.BuildKategorienTitelListe();
appCache.Insert(CachedData.NaviDataKey, katList, null, DateTime.Now.AddDays(1d), Cache.NoSlidingExpiration);
}
}
}
return katList;
}
答案 1 :(得分:0)
MSDN documentation 声明ASP.NET Cache类是线程安全的 - 这意味着它们的内容可以由AppDomain中的任何线程自由访问(例如,读/写将是原子的)。
请记住,随着缓存大小的增加,同步成本也会增加。您可能需要查看this帖子
通过添加私有对象来锁定,您应该能够安全地运行您的方法,以便其他线程不会干扰。
private static readonly myLockObject = new object();
public static List<Kategorie> GetKategorieTitelListe(Cache appCache)
{
// get Data out of Cache
List<Kategorie> katList = appCache[CachedData.NaviDataKey] as List<Kategorie>;
lock (myLockObject)
{
// Cache expired, retrieve and store again
if (katList == null)
{
katList = DataTools.BuildKategorienTitelListe();
appCache.Insert(CachedData.NaviDataKey, katList, null, DateTime.Now.AddDays(1d), Cache.NoSlidingExpiration);
}
return katList;
}
}
答案 2 :(得分:0)
除了锁定,我没有看到其他解决方案。
private static readonly object _locker = new object ();
public static List<Kategorie> GetKategorieTitelListe(Cache appCache)
{
List<Kategorie> katList;
lock (_locker)
{
// get Data out of Cache
katList = appCache[CachedData.NaviDataKey] as List<Kategorie>;
// Cache expired, retrieve and store again
if (katList == null)
{
katList = DataTools.BuildKategorienTitelListe();
appCache.Insert(CachedData.NaviDataKey, katList, null, DateTime.Now.AddDays(1d), Cache.NoSlidingExpiration);
}
}
return katList;
}
一旦数据进入缓存,并发线程将只等待获取数据的时间,即这行代码:
katList = appCache[CachedData.NaviDataKey] as List<Kategorie>;
因此,性能成本不会太大。