用于频繁读取和罕见写入操作的并发收集(.NET)

时间:2014-06-07 19:16:42

标签: c# .net multithreading collections concurrent-collections

我想在我的Web应用程序中创建一个缓存,这将允许顶层(MVC)持久保存从底层(服务和数据库)检索的一些值,以避免对它的不必要的请求。我希望在网站的每个页面上存储我想要存储在缓存中的数据,因此该缓存旨在显着减少请求量。这个想法是很多线程将从集合中读取数据,而一个线程将清除它并在缓存过期后检索新数据。这意味着该集合将用于频繁阅读罕见写作操作。

对我来说,问题是为这些目的选择合适的类。我已经阅读了.NET 4中引入的System.Collections.Concurrent并发类的集合。我绝对不需要使用ConcurrentQueueConcurrentStack,但我必须在{{{{}}之间进行选择。 3}},BlockingCollectionConcurrentBag

在这种情况下,

ConcurrentBag似乎是最好的解决方案。但是,我在ConcurrentDictionary中读到并发字典

  

...对于读取操作完全无锁。通过这种方式,它针对从字典中读取最频繁操作的场景进行了优化。

所以也许最好的解决方案是使用ConcurrentDictionary<int, MyTypeOfObj>代替?或者我可能根本不需要并发类型,简单的List会完成这项工作吗?可能,如果我可以以某种方式锁定缓存更新时的操作,它会这样做。但是使用简单的lock是不可取的。

感谢任何建议和解释。

更新

缓存用于存储插座的地图点。这组插座非常稳定,但应该有一个UI来添加它们,因此插入操作非常少见。在超时后从底层执行整个集合然后执行插入操作可能更容易。也不需要搜索,阅读意味着简单的枚举。

1 个答案:

答案 0 :(得分:0)

根据对问题的评论,我决定使用MemoryCache(事实上,我并不十分清楚)。由于我不需要为数据分隔缓存实例,因此我使用保存在Default属性中的缓存:

  

除非需要,否则不要创建MemoryCache实例。 (...)如果在应用程序中只创建一个缓存实例,请使用默认缓存,并在需要访问缓存时从Default属性获取对它的引用。

Link (MSDN)

按以下方式初始化缓存I:

private static MemoryCache _instance = MemoryCache.Default;

public static IEnumerable<MyDto> GetDtos()
{
    var result = _instance.Get(SOME_NAME);
    if (result == null)
        result = InitializeCache();
    return result;
}

没有用于阅读操作的锁。然后,在InitializeCache中锁定一个以防止多个请求:

private static IEnumerable<MyDto> InitializeCache()
{
    lock (_monitor) // here all threads but one will stop
    {
        var result = _instance.Get(SOME_NAME); // in case this thread is not the first who entered the lock
        if (result == null)
        {
            result = RetrieveSomeHowYourDataFromDbOrService();
            if (result == null)
                return null; // or exception
            _instance.Set(SOME_NAME, result, new CacheItemPolicy { AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddSeconds(TIMEOUT)), RemovedCallback = CacheClearedCallback}); 
        }
        return (IEnumerable<MyDto>)result;
    }
}

使用CacheItemPolicy允许清除过时的项目并从回调中再次检索项目:

private static void CacheClearedCallback(CacheEntryRemovedArguments arguments)
{
    InitializeCache();
}

在这种情况下,缓存将在没有来自消费者的请求的情况下进行更新(而不是在应用程序的第一个请求中进行)。

可能这不是有史以来最好的解决方案,但希望对某人有帮助,也许可以从一开始。感谢您的提示和意见。