锁定以将数据加载到缓存
本文关键字:缓存 加载 数据 锁定 | 更新日期: 2023-09-27 18:27:19
我在一个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
}
我的问题是,锁有帮助吗?我正在努力减少对数据库的调用,但锁定只是引入了开销吗?缓存的数据在整个网站上使用非常普遍,而且几乎永远不会改变。还有,我锁在正确的地方了吗?
如果你锁定,我会使用双重检查锁定,这样你就不会在每次从缓存读取时产生获取锁定的开销。
我也会质疑是否需要上锁。如果不锁定,那么多个线程可能会同时尝试刷新缓存,但这种情况很少见,除非非常昂贵,否则不会给静态只读数据带来问题。
如果是昂贵的,那么您可以按照@asawyer评论中的建议,在缓存中插入一个Lazy<ReadOnlyCollection<Location>>
,这样锁定将由Lazy<T>
处理。
由于对服务器的每个web请求都在创建一个新线程,并且您的静态助手在这些线程之间共享,因此某种类型的锁很有用。如果没有锁,当数据库读取已经在进行时,您将面临进入EnsureLocationsCache方法的风险。现在,这可能不会影响两次加载的数据的正确性,但如果DB读取成本高昂,它将影响您的整体性能,并抵消缓存的影响。
这实际上取决于应用程序启动时尝试访问EnsureLocationsCache()方法的并发线程的数量,这可能很低,因为这是对每个LocationCacheKey的一次性调用。
获取锁的开销是一个值得关注的问题,因为即使缓存已经加载,代码也会获取锁@asawyer、@TomTom和@Joe提出了一些替代方案。
编辑:
请考虑在静态构造函数中调用EnsureLocationsCache()。在这种情况下,您根本不需要锁。
参见关于静态构造函数线程安全的讨论。