缓存和线程安全
本文关键字:安全 线程 缓存 | 更新日期: 2023-09-27 18:04:10
我在一个ASP缓存数据。. NET网站通过System.Web.Caching。缓存类,因为检索数据的成本非常高,而且只有当我们的内容人员在后端更改数据时,它才会偶尔更改。
所以我在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;
}
我看到这个代码的问题是,它不是线程安全的。如果两个用户同时打开其中两个页面,并且缓存刚刚用完,则存在多次检索数据的风险。
但是如果我锁定方法体,就会遇到性能问题,因为一次只有一个用户可以获得数据列表。
是否有简单的方法来防止这种情况?这种情况下最好的做法是什么?
你是对的,你的代码不是线程安全的。
// 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;
}
MSDN文档声明ASP。. NET缓存类是线程安全的——这意味着它们的内容可以被AppDomain中的任何线程自由访问(例如,读/写将是原子的)。
请记住,随着缓存大小的增长,同步成本也会增加。你可能想看看这篇文章
通过添加一个私有对象来锁定,你应该能够安全地运行你的方法,这样其他线程就不会干扰。
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;
}
}
我看不出除了锁以外还有什么办法。
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>;
所以性能成本不会太大