锁定以将数据加载到缓存

时间:2012-03-08 13:59:29

标签: c# asp.net .net-4.0 locking

我在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
}

我的问题是,锁定有帮助吗?我正在尝试减少对数据库的调用,但锁定只是引入了开销吗?缓存的数据在整个站点中非常常用,几乎不会改变。我也锁定在正确的位置?

2 个答案:

答案 0 :(得分:2)

如果你做了锁定,我会使用double-checked locking,这样你每次从缓存中读取时都不会产生获取锁的开销。

我还要质疑是否需要锁定。如果你没有锁定,那么多个线程可能会尝试同时刷新缓存,但这种情况很少见,除非它非常昂贵,否则不会对静态只读数据造成问题。

如果 昂贵,那么您可以按照@asawyer的评论中的建议并将Lazy<ReadOnlyCollection<Location>>插入缓存中,以便锁定将由Lazy<T>处理

答案 1 :(得分:1)

由于每个Web服务器请求都在创建一个新线程,并且您的静态助手在这些线程之间共享,因此某种类型的锁是有用的。如果没有锁定,当数据库读取已在进行中时,您可能会冒险进入EnsureLocationsCache方法。现在这可能不会影响两次加载数据的正确性,但如果数据库读取费用昂贵,则会影响整体性能并消除缓存的影响。

这实际上取决于尝试访问应用程序启动的EnsureLocationsCache()方法的并发线程的数量,这可能很低,因为它是对每个LocationCacheKey的一次性调用。

获取锁的开销是一个有效的问题,因为即使缓存已经加载,代码也会获取锁。 @asawyer,@ TomTom和@Joe提出了一些替代方案。

编辑:

考虑在 static constructor 中调用EnsureLocationsCache()。在这种情况下,您根本不需要锁定。

See discussion on thread-safety of static constructors