在Web应用程序中正确使用C#lock

时间:2013-03-06 12:12:32

标签: asp.net-mvc locking

我有一个帮助器类,它读取一个大的XML文档并生成一个c#对象列表。

我使用这些对象非常多,所以我认为最好的方法是将它们保存在内存中,然后从那里访问它们。

我创建了一个简单的存储库,它从内存中获取一个对象,如果不存在,则添加它。

存储库看起来像这样:

public class XmlDocumentRepository
{
    private readonly ICacheStorage _cacheStorage;
    public XmlDocumentRepository(ICacheStorage cacheStorage)
    {
        _cacheStorage = cacheStorage;
    }

    private readonly object _locker = new object();

    private void DeserializeXmlDocument()
    {
        lock (_locker)
        {
            // I deserialize the xml document, i generate the c# classes, and save them in cache
            IEnumerable<Page> pages = new XmlDeserializerHelper().DeserializeXml();    

            foreach(var page in pages)
            {
                _cacheStorage.Add(page_Id, page);
            }
        }
    }

    public Page GetPage(Guid page_Id)
    {
        Page page = _cacheStorage.Get<Page>(page_Id);
        if (page != null)
            return page;

        lock (_locker)
        {
            page = _cacheStorage.Get<Page>(page_Id);
            if (page != null)
                return page;

            DeserializeXmlDocument();

            page = _cacheStorage.Get<Page>(page_Id);
            return page;
        }
    }
}

XmlDocumentRepository在Web应用程序中使用(更真实的是asp.net mvc)。

存储库的实现是否良好?我正在使用lock语句吗?

2 个答案:

答案 0 :(得分:1)

在我对这个问题的评论中,我误解了共享的缓存。我认为您需要执行以下选项之一:

  • 使XmlDocumentRepository成为在所有请求中使用的单例,因为锁对象是私有字段,因此每个请求都会有一个带有新字段的存储库的新实例。
  • 使锁定对象成为静态字段,以便在所有XmlDocumentRepository实例之间共享。

答案 1 :(得分:0)

作为主要规则,您希望保护多个线程使用的数据存储的所有访问变体。我看到你的实施有几个潜在的问题;

1:IChacheStorage是从外部提供的,这意味着可以在其他地方修改此集合,这可能会或可能不会受到锁的保护。也许您应该要求集合本身在内部使用锁定,或者其他类型的线程安全机制?

2:您对数据访问的锁定保护不一致。在GetPage中,您在应用锁之前访问_cacheStorage,而在反序列化中,您可以在锁内访问它。这意味着您可能会得到一个结果,其中一个正在添加到缓存而另一个正在从缓存中获取。

3:您是否需要缓存,xml读取或两者的线程安全性? 如果您只需要保护缓存,请在锁外移动读取xml。如果同时保护两者,则应将整个GetPage函数放在锁中。