我在Web应用程序中有一个帮助器类,它所做的是将常见的,不变的数据对象呈现为静态属性。我正在加载这些对象如下:
public static class MyWebHelper
{
#region - Constants & Fields
private const string LocationCacheKey = "MyWebHelper_Locations";
private static object LocationLoadLock = new object();
private static MemoryCache m_SiteCache;
#endregion
#region - Properties
/// <summary>
/// Gets the uneditable collection of locations.
/// </summary>
public static ReadOnlyCollection<Location> Locations
{
get
{
EnsureLocationsCache();
return (ReadOnlyCollection<Location>)SiteCache[LocationCacheKey];
}
}
/// <summary>
/// Gets the MemoryCache object specific for my site cache.
/// </summary>
public static MemoryCache SiteCache
{
get
{
if (m_SiteCache == null)
{
m_SiteCache = new MemoryCache("MyWeb_SiteCache");
}
return m_SiteCache;
}
}
#endregion
#region - Methods
private static void EnsureLocationsCache()
{
lock (LocationLoadLock)
{
if (SiteCache[LocationCacheKey] == null)
{
//
// Load all Locations from the database and perform default sorting on location code.
//
List<Location> lLocations = DataAccess.GetAllLocations();
lLocations.Sort(delegate(Location l1, Location l2) { return string.CompareOrdinal(l1.Code, l2.Code); });
SiteCache[LocationCacheKey] = new ReadOnlyCollection<Location>(lLocations);
}
}
}
#endregion
}
我的问题是,锁定有帮助吗?我正在尝试减少对数据库的调用,但锁定只是引入了开销吗?缓存的数据在整个站点中非常常用,几乎不会改变。我也锁定在正确的位置?
答案 0 :(得分:2)
如果你做了锁定,我会使用double-checked locking,这样你每次从缓存中读取时都不会产生获取锁的开销。
我还要质疑是否需要锁定。如果你没有锁定,那么多个线程可能会尝试同时刷新缓存,但这种情况很少见,除非它非常昂贵,否则不会对静态只读数据造成问题。
如果 昂贵,那么您可以按照@asawyer的评论中的建议并将Lazy<ReadOnlyCollection<Location>>
插入缓存中,以便锁定将由Lazy<T>
处理
答案 1 :(得分:1)
由于每个Web服务器请求都在创建一个新线程,并且您的静态助手在这些线程之间共享,因此某种类型的锁是有用的。如果没有锁定,当数据库读取已在进行中时,您可能会冒险进入EnsureLocationsCache方法。现在这可能不会影响两次加载数据的正确性,但如果数据库读取费用昂贵,则会影响整体性能并消除缓存的影响。
这实际上取决于尝试访问应用程序启动的EnsureLocationsCache()方法的并发线程的数量,这可能很低,因为它是对每个LocationCacheKey的一次性调用。
获取锁的开销是一个有效的问题,因为即使缓存已经加载,代码也会获取锁。 @asawyer,@ TomTom和@Joe提出了一些替代方案。
编辑:
考虑在 static constructor 中调用EnsureLocationsCache()。在这种情况下,您根本不需要锁定。